I'm making an application which I need to give access to the file system (fs) module, however even with nodeIntegration
enabled the renderer gives me this error:
Uncaught ReferenceError: require is not defined
All similar problems I could find had a solution that said they needed to turn nodeIntegration
on, however I already have it enabled.
This is my main.js:
const electron = require('electron');
const {app, BrowserWindow} = electron;
let win;
app.on('ready', () => {
var { width, height } = electron.screen.getPrimaryDisplay().workAreaSize;
width = 1600;
height = 900;
win = new BrowserWindow({'minHeight': 850, 'minWidth': 1600, width, height, webPreferences: {
contextIsolation: true,
webSecurity: true,
nodeIntegration: true
}});
win.setMenu(null);
win.loadFile('index.html');
win.webContents.openDevTools()
});
My index.js, linked in index.html as <script src="index.js"></script>
currently only has require("fs");
in it, I've commented out all the other stuff.
I don't know why require still doesn't work even though nodeIntegration
is enabled.
To solve the "ReferenceError require is not defined" error, remove the type property if it's set to module in your package. json file and rename any files that have a . mjs extension to have a . js extension.
Electron node integration refers to the ability of accessing Node. js resources from within the “renderer” thread (the UI). It is enabled by default in Quasar CLI, although Electron is encouraging developers to turn it off as a security precaution.
@electron/remote is an Electron module that bridges JavaScript objects from the main process to the renderer process. This lets you access main-process-only objects as if they were available in the renderer process.
Summary A preload script contains code that runs before your web page is loaded into the browser window. It has access to both DOM APIs and Node. js environment, and is often used to expose privileged APIs to the renderer via the contextBridge API.
When you have nodeIntegration
disabled but aren't using contextIsolation
, you could use a preload script to expose a safe version of it on the global object. (Note: you shouldn't expose the entire fs
module to a remote page!)
Here's an example of using a preload script in this way:
// main process script
const mainWindow = new BrowserWindow({
webPreferences: {
contextIsolation: false,
nodeIntegration: false,
preload: './preload.js'
}
})
mainWindow.loadURL('my-safe-file.html')
// preload.js
const { readFileSync } = require('fs')
// the host page will have access to `window.readConfig`,
// but not direct access to `readFileSync`
window.readConfig = function () {
const data = readFileSync('./config.json')
return data
}
// renderer.js
const config = window.readConfig()
If you're only loading local pages, and those pages don't load or execute unsafe dynamic content then you might reconsider the use of contextIsolation
for this strategy. If you want to keep contextIsolation
on, however (and you definitely should if you have a chance of showing unsafe content), you can only communicate with the preload script with message passing via postMessage
.
Here's an example of the same scenario above, but with contextIsolation
on and using message passing.
// main process script
const mainWindow = new BrowserWindow({
webPreferences: {
contextIsolation: true,
nodeIntegration: false,
preload: './preload.js'
}
})
mainWindow.loadURL('my-unsafe-file.html')
// preload.js
const { readFileSync } = require('fs')
const readConfig = function () {
const data = readFileSync('./config.json')
return data
}
window.addEventListener('message', (event) => {
if (event.source !== window) return
if (event.data.type === 'request') {
window.postMessage({ type: 'response', content: readConfig() })
}
})
// renderer.js
window.addEventListener('message', (event) => {
if (event.source !== window) return
if (event.data.type === 'response') {
const config = event.data.content
}
})
window.postMessage('request')
While this is definitely more verbose and difficult to deal with (and forces things to be async, because message passing is async), it's also much more secure. A pair of small JS wrappers around the postMessage
API could make this easier to work with (e.g. via an RPC-like mechanism), but remember that the whole point of using contextIsolation
is because you can't trust the renderer, so your preload script shouldn't trust just any message it gets via the postMessage
API — you should always verify the event that you receive to ensure that you trust it.
This slide deck describers in detail why turning off Node integration without using context isolation is not always a good idea.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With