Electron - require is not defined, despite nodeIntegration being true - javascript

my problem is - I got a main.js file, and a functions.js file, where I want some of my functions to be. However, whenever I use require in that functions.js file, I get an error, require is not defined.
I've read posts about other people having similar problem, but in their case setting nodeIntegration to true helped. That's what I had from the beginning. I know the problems of this solution, but at the moment I don't need the app to be secure, so I would be fine with this solution, if it worked. It doesn't.
I tried preloading, but I think it's just a "safer" equivalent of nodeIntegration solution. And being an equivalent, it doesn't work either. Can you help me?
My code:
main.js
const { app, BrowserWindow, ipcMain } = require('electron')
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
contextIsolation: true,
preload: 'functions.js'
}
})
win.maximize();
win.loadFile('index.html');
}
app.on('ready', function() {
createWindow();
createOpenDialog();
});
functions.js (literally, this is all it takes for the code to fail)
const electron = require('electron');
index.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="main.css">
<meta charset="UTF-8">
<title>Hello World!</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
</head>
<body style="background: white;">
<script src="functions.js">
</script>
</body>
</html>

So, one thing I didn't try was setting contextIsolation to false. And least it seems like it, because I was sure I tried it. Anyway, this seems to fix the issue, although I will need to learn what it does exactly. I can do "require" now, and already tested if it works by using ipcRenderer.

Context isolation is a security feature that exists on Electron. Its whole purpose is to separate the preload scripts and Electrons internal apis from your website so it doesn't have any access it should not have.
From Electron 12 context isolation is off by default .This means that if you need to expose some functionality in your preload scripts you will have to use context bridge
An example from the documentation is this :
Before context isolation was off
window.myAPI = {
doAThing: () => {}
}
After : context isolation is on
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('myAPI', {
doAThing: () => {}
})
You can read more about this feature in the docs :
https://www.electronjs.org/docs/tutorial/context-isolation

Related

Uncaught ReferenceError: require is not defined while running the ElectronJs application [duplicate]

I'm creating an Electron app for my own purpose. My problem is when I'm using node functions inside my HTML page it throws an error of:
'require()' is not defined.
Is there any way to use Node functionalities in all my HTML pages? If it is possible please give me an example of how to do this or provide a link. Here are the variables I'm trying to use in my HTML page:
var app = require('electron').remote;
var dialog = app.dialog;
var fs = require('fs');
and these are the values I'm using in all my HTML windows within Electron.
As of version 5, the default for nodeIntegration changed from true to false.
You can enable it when creating the Browser Window:
app.on('ready', () => {
mainWindow = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
}
});
});
Edit 2022
I've published a larger post on the history of Electron and it's security that provides additional context on the changes that affect how security was approached in different framework versions (and what's the best approach to take).
Original answer
I hope this answer gets some attention, because a large majority of answers here leave large security holes in your electron app. In fact this answer is essentially what you should be doing to use require() in your electron apps. (There is just a new electron API that makes it a little bit cleaner in v7).
I wrote a detailed explanation/solution in github using the most current electron apis of how you can require() something, but I'll explain briefly here why you should follow an approach using a preload script, contextBridge and ipc.
The problem
Electron apps are great because we get to use node, but this power is a double-edged sword. If we are not careful, we give someone access to node through our app, and with node a bad actor can corrupt your machine or delete your operating system files (among other things, I imagine).
As brought up by #raddevus in a comment, this is necessary when loading remote content. If your electron app is entirely offline/local, then you are probably okay simply turning on nodeIntegration:true. I still would, however, opt to keep nodeIntegration:false to act as a safeguard for accidental/malicious users using your app, and prevent any possible malware that might ever get installed on your machine from interacting with your electron app and using the nodeIntegration:true attack vector (incredibly rare, but could happen)!
What does the problem look like
This problem manifests when you (any one of the below):
Have nodeIntegration:true enabled
Use the remote module
All of these problems give uninterrupted access to node from your renderer process. If your renderer process is ever hijacked, you can consider all is lost.
What our solution is
The solution is to not give the renderer direct access to node (ie. require()), but to give our electron main process access to require, and anytime our renderer process needs to use require, marshal a request to the main process.
The way this works in the latest versions (7+) of Electron is on the renderer side we set up ipcRenderer bindings, and on the main side we set up ipcMain bindings. In the ipcMain bindings we set up listener methods that use modules we require(). This is fine and well because our main process can require all it wants.
We use the contextBridge to pass the ipcRenderer bindings to our app code (to use), and so when our app needs to use the required modules in main, it sends a message via IPC (inter-process-communication) and the main process runs some code, and we then send a message back with our result.
Roughly, here's what you want to do.
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, a secure template to build electron apps. I care about this topic, and have been working on this for a few weeks (at this point in time).
For security reasons, you should keep nodeIntegration: false and use a preload script to expose just what you need from Node/Electron API to the renderer process (view) via window variable. From the Electron docs:
Preload scripts continue to have access to require and other Node.js features
Example
main.js
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(app.getAppPath(), 'preload.js')
}
})
preload.js
const { remote } = require('electron');
let currWindow = remote.BrowserWindow.getFocusedWindow();
window.closeCurrentWindow = function(){
currWindow.close();
}
renderer.js
let closebtn = document.getElementById('closebtn');
closebtn.addEventListener('click', (e) => {
e.preventDefault();
window.closeCurrentWindow();
});
First off, #Sathiraumesh solution leaves your electron application with huge security issue. Imagine that your app is adding some extra features to messenger.com, for example toolbar's icon will change or blink when you've have unread message. So in your main.js file, you create new BrowserWindow like so (notice I intentionally misspelled messenger.com):
app.on('ready', () => {
const mainWindow = new BrowserWindow({
webPreferences: {
nodeIntegration: true
}
});
mainWindow.loadURL(`https://messengre.com`);
});
What if messengre.com is a malicious website, that wants to harm your computer. If you set nodeIntegration: true this site has access to your local file system and can execute this:
require('child_process').exec('rm -r ~/');
And your home directory is gone.
Solution
Expose only what you need, instead of everything. This is achived by preloading javascript code with require statements.
// main.js
app.on('ready', () => {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: `${__dirname}/preload.js`
}
});
mainWindow.loadURL(`https://messengre.com`);
});
// preload.js
window.ipcRenderer = require('electron').ipcRenderer;
// index.html
<script>
window.ipcRenderer.send('channel', data);
</script>
Now awful messengre.com cannot delete your entire file system.
It looks like Electron's security evolved like this (source).
Electron 1 nodeIntegration defaults to true
Renderer has full access to Node API -- huge security risks if Renderer loads remote code.
Electron 5 nodeIntegration defaults to false
When set to false, a preload script is used to expose specific API to Renderer. (The preload script always has access to Node APIs regardless of the value of nodeIntegration)
//preload.js
window.api = {
deleteFile: f => require('fs').unlink(f)
}
Electron 5 contextIsolation defaults to true (actually still defaults to false in Electron 11)
This causes preload script to run in a separate context. You can no longer do window.api = .... You now have to do:
//preload.js
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('api', {
deleteFile: f => require('fs').unlink(f)
})
Electron 6 require()ing node builtins in sandboxed renderers no longer implicitly loads the remote version
If Renderer has sandbox set to true, you have to do:
//preload.js
const { contextBridge, remote } = require('electron')
contextBridge.exposeInMainWorld('api', {
deleteFile: f => remote.require('fs').unlink(f)
})
Electron 10 enableRemoteModule default to false (remote module deprecated in Electron 12)
The remote module is used when you need to access Node APIs from a sandboxed Renderer (as in above example); or when you need to access Electron APIs that are available only to the Main process (such as dialog, menu). Without remote, you'll need to write explicit IPC handlers like follows.
//preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('api', {
displayMessage: text => ipcRenderer.invoke("displayMessage", text)
})
//main.js
const { ipcMain, dialog } = require('electron')
ipcMain.handle("displayMessage", text => dialog.showMessageBox(text))
Electron 10 deprecate nodeIntegration flag (removed in Electron 12)
Recommendation
Always set {nodeIntegration: false, contextIsolation: true, enableRemoteModule: false}.
For max security, set {sandbox: true}. Your preload script will have to use IPC to call the Main process to do everything.
If sandbox is false, your preload script can access Node API directly, as in require('fs').readFile. You're secure as long as you don't this:
//bad
contextBridge.exposeInMainWorld('api', {
readFile: require('fs').readFile
})
Are you using nodeIntegration: false while BrowserWindow initialization? If so, set it to true (defaults value is true).
And include your external scripts in the HTML like this (not as <script> src="./index.js" </script>):
<script>
require('./index.js')
</script>
All I wanted to do was to require a js file in my html page because of the tutorial I was following. However, I intend to use remote modules so security was paramount. I modified Michael's answer up there so I'm posting, purely for those who spent hours looking for a secure alternative to 'require' like me. If the code is incorrect, feel free to point it out.
main.js
const electron = require('electron');
const app=electron.app;
const BrowserWindow=electron.BrowserWindow;
const ipcMain=electron.ipcMain;
const path=require('path');
const url=require('url');
let win;
function createWindow(){
win=new BrowserWindow({
webPreferences:{
contextIsolation: true,
preload: path.join(__dirname, "preload.js")
}
});
win.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file',
slashes: true
}));
win.on('close', function(){
win=null
});
}
app.on('ready', createWindow);
preload.js
const electron=require('electron');
const contextBridge=electron.contextBridge;
contextBridge.exposeInMainWorld(
"api", {
loadscript(filename){
require(filename);
}
}
);
index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello World App</title>
</head>
<body>
<h1>Hello World</h1>
<button id="btn">Click</button>
</body>
<script>
window.api.loadscript('./index.js');
</script>
</html>
index.js
const btn = document.getElementById('btn');
btn.addEventListener('click', function(){
console.log('button clicked');
});
I am especially curious to know if this still presents a security risk. Thanks.
If you just don't care about any security issues and want to have require being interpreted correctly by JavaScript on the browser window, then have an extra flag on the main.js code:
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true,
nodeIntegrationInSubFrames: true,
enableRemoteModule: true,
contextIsolation: false //required flag
}
//rest of the code...
You have to enable the nodeIntegration in webPreferences to use it. see below,
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({
webPreferences: {
nodeIntegration: true
}
})
win.show()
There was a breaking api changes in electron 5.0(Announcement on Repository). In recent versions nodeIntegration is by default set to false.
Docs Due to the Node.js integration of Electron, there are some extra symbols inserted into the DOM like module, exports, require. This causes problems for some libraries since they want to insert the symbols with the same names.To solve this, you can turn off node integration in Electron:
But if you want to keep the abilities to use Node.js and Electron APIs, you have to rename the symbols in the page before including other libraries:
<head>
<script>
window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;
</script>
<script type="text/javascript" src="jquery.js"></script>
</head>
For sake of actuality and completeness I am adding my piece of cake. Here is what I find important about this topic. Please keep in mind the date of this post - October 2022, the version of electron is 21.1.1.
There is an article in electron docs called Inter-Process Communication where this topic is described in a very clear way.
The following code is just a copy of the example code on that aforementioned site.
The main.js file:
const {app, BrowserWindow, ipcMain} = require('electron')
const path = require('path')
function createWindow () {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
ipcMain.on('set-title', (event, title) => {
const webContents = event.sender
const win = BrowserWindow.fromWebContents(webContents)
win.setTitle(title)
})
mainWindow.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
The takeaway:
in webPreferences define only the preload script and leave all those nodeIntegration, nodeIntegrationInWorker, nodeIntegrationInSubFrames, enableRemoteModule, contextIsolation apply the defaults.
The next file is preload.js:
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
setTitle: (title) => ipcRenderer.send('set-title', title)
})
Here the electronAPI object will be injected into the browsers context so there will be a window.electronAPI object which will have a member function called setTitle. Of course you can add whatever other properties there.
The setTitle function only calls ipcRenderer.send which is one end of the Inter-Process Communication brigde or tunnel if you like.
What you send in here falls out on the other end, which is in the main.js file, the ipcMain.on function. Here you register for the set-title event.
The example continues with the index.html file:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>Hello World!</title>
</head>
<body>
Title: <input id="title"/>
<button id="btn" type="button">Set</button>
<script src="./renderer.js"></script>
</body>
</html>
which loads the renderer.js script:
const setButton = document.getElementById('btn')
const titleInput = document.getElementById('title')
setButton.addEventListener('click', () => {
const title = titleInput.value
window.electronAPI.setTitle(title)
});
and there you can access the window.electronAPI.setTitle function, which you defined in preload.js where it sends the title into ipcRenderer and this title then falls out of ipcMain in main.js fireing an event and causing a function to run which in turn sets the application title.
So once again I want to emphasize to read the documentation. There is more about IPC with exanples. Also read the Context Isolation chapter, it is short and very clear.
Finally, I made it work.Add this code to your HTML document Script Element.
Sorry for the late Reply.I use the below code to do this thing.
window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;
And use nodeRequire instead of using require.
It works Fine.

Define CSP HTTP Header in Electron App

Following the API documentation, I don't understand how to define a Content-Security-Policy HTTP Header for the renderer of my Electron application. I always get a warning in the DevTools.
I tried:
1) Copy/Paste the code in the API Doc, blindly:
app.on('ready', () => {
const {session} = require('electron')
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({responseHeaders: `default-src 'self'`})
})
win = new BrowserWindow(...)
win.loadUrl(...)
}
(By the way, I don't get why "Content-Security-Policy:" is missing in the string. But adding it don't change anything)
2) Modifying the session of the renderer with the same code:
win = new BrowserWindow(...)
win.loadUrl(...)
const ses = win.webContents.session;
ses.webRequest.onHeadersReceived((details, callback) => {
callback({responseHeaders: `default-src 'self'`})
})
3) Add an extra header to ther renderer:
win = new BrowserWindow(...)
win.loadURL(`file://${__dirname}/renderer.html`,{
extraHeaders: `Content-Security-Policy: default-src 'self'`
});
...
The only thing that works is using a meta tag in the renderer HTML file:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'>
Not sure why the documentation contains this broken code. It confused the hell out of me but I found a working solution by trial and error:
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({ responseHeaders: Object.assign({
"Content-Security-Policy": [ "default-src 'self'" ]
}, details.responseHeaders)});
});
So the headers argument must be an object with the same structure as the original headers received in details.responseHeaders. And the original headers must be included in the passed object as well because this object seems to completely replace the original response headers.
The extraHeaders option isn't for response headers. It is for request headers sent to the server.
If your aim is to be able to use CSP in both dev mode (with resources loaded by http:// protocol) and prod mode (file:// protocol) here's how you can do it:
First, remove the Content-Security-Policy meta from src/index.html - we need to inject it only for prod mode, because
onHeadersReceived will not work for file:// protocol as Electron docs confirm, and also because
if we keep it in src/index.html for Dev mode it will override the onHeadersReceived at least for part of resources, and for Dev mode we need different settings.
Then we can inject it for Prod mode with gulp-inject:
// in project dir
npm install --save-dev gulp-inject gulp
// src/index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<base href="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- inject:prod-headers -->
<!-- src/prod-headers.html content will be injected here -->
<!-- endinject -->
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root>Loading...</app-root>
</body>
</html>
// src/prod-headers.html
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
// gulpfile.js
var gulp = require('gulp');
var inject = require('gulp-inject');
gulp.task('insert-prod-headers', function () {
return gulp.src('./dist/index.html')
.pipe(inject(gulp.src('./src/prod-headers.html'), {
starttag: '<!-- inject:prod-headers -->',
transform: function (filePath, file) {
// return file contents as string
return file.contents.toString('utf8')
}
}))
.pipe(gulp.dest('./dist'));
});
Then make sure npx gulp insert-prod-headers is run after e.g. ng build generates dist/index.html.
And for dev mode let's use onHeadersReceived similarly to Electron docs example:
const args = process.argv.slice(1);
const devMode = args.some((val) => val === '--serve');
app.on('ready', () => {
if (devMode) {
const {session} = require('electron')
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({responseHeaders: `default-src http: ws:`})
})
}
win = new BrowserWindow(...)
win.loadUrl(...)
}
This solution was tested on Electron 4.0.3.
As pointed out in the Electron docs, you will have to use a Content Security Policy (CSP) Meta Tag in the html file when you load your renderer.html via file:// scheme (IIRC you do that in above example).
If you want to adjust the content security policy conditionally for prod and dev environment, you can dynamically generate this string inside the html in your build step. I suggest using a template engine like mustache.js (used in the example).
Example (file resources)
In my case I wanted to enable Hot Module Replacement (HMR) via websockets and file:// resource in dev mode, which required relaxing the CSP rules (but only in dev!).
index.mustache:
<html>
<head>
<meta
http-equiv="Content-Security-Policy"
content="{{{cspContent}}}"
/>
</head>
...
cspContent.json for dev:
{
"cspContent": "default-src 'self'; connect-src 'self' ws:"
}
build step in dev (for prod default values could be used):
npx mustache cspContent.json index.mustache > index.html
URL resources
For usage with URL resources, you can stick to this example:
const { session } = require('electron')
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': ['default-src \'none\'']
}
})
})
Make sure to merge your custom CSP response headers with the default ones - you don't do that in your pasted example above. Here, you can also check conditionally for the environment.
Hope, it helps.
There isn't enough detail in your question to know whether you are having issues on initial load or subsequent web requests, but my issue was for the initial file load. With an Electron app using React, I was getting warnings about using inline scripts even with kayahr's code. This is because the onHeadersReceived method only catches requests that are made after the application has loaded initially. It will not stop any warnings from the initial application load.
I ended up having to use templating during my application build to add a nonce to the inline script and style and to the CSP header in the HTML file that the application loads initially.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-<%= scriptNonce %>'; style-src 'nonce-<%= styleNonce %>';">
<link rel="stylesheet" type="text/css" href="./index.css" nonce=<%= styleNonce %>>
<title>Basic Electron App</title>
</head>
<body>
<div id="app"></div>
<script type="application/javascript" nonce=<%= scriptNonce %>>
require('./index.js');
</script>
</body>
</html>
index.css
body {
margin: 0px;
}
.hello {
font-family: "Century Gothic";
width: 800px;
margin: 70px auto;
text-align: center;
}
and in gulfile.js add the following to what you already have and make sure this task is included in your pipeline. You can also just update your current html task with the code below.
const template = require('gulp-template');
const uuidv4 = require('uuid/v4');
gulp.task('copy-html', () => {
// Create nonces during the build and pass them to the template for use with inline scripts and styles
const nonceData = {
scriptNonce: new Buffer(uuidv4()).toString('base64'),
styleNonce: new Buffer(uuidv4()).toString('base64')
};
return gulp.src('src/*.html')
.pipe(template(nonceData))
.pipe(gulp.dest('dist/'));
});
This is a very stripped down example. I have a more complete example at https://github.com/NFabrizio/data-entry-electron-app if anyone is interested, though there is still one warning when running the application because one of the packages I am using pulls in react-beautiful-dnd, which adds inline styles but does not currently accept nonces.
Set the following meta tag in the renderers.
<meta http-equiv="Content-Security-Policy" content="script-src 'nonce-xxx or sha256-yyy' " />
Kindly checkout my github repo electron-renderer-CSP-sample, containing samples for both nonce & SHA methods for internal & external js files as well.

Unable to connect to Openfire server from the script running browser side

At first, I was connecting to Openfire using following script.
const {Client} = require('#xmpp/client')
const client = new Client()
client.start('xmpp://localhost:5222').catch(err => {
console.error('start failed', err)
})
client.handle('authenticate', authenticate => {
return authenticate('gabbar', 'gabbar#123')
})
But it shows me an error 'require is not defined'. so I searched the internet and found that browserify could do my work. so I made the bundle.js file using index.js of my HTML page and included it in the HTML page.
<head>
<meta charset="UTF-8"/>
<title>xmpp.js example</title>
<script src="bundle.js"></script>
<!-- <script src="index.js"></script>-->
</head>
but then I am getting the error
no compatible connection method found
Is anybody can tell any other way of doing it. I tried also same as given in example directory of xmpp.js client package, but that is giving me error like XMPP is not a function. Following is the code which I wrote after looking at example files.
index.js
const {xmpp, xml} =
typeof require === 'undefined' ? window.xmpp : require('#xmpp/client') // For you; require('#xmpp/client')
const {client} = xmpp()
client.start('xmpp://localhost:5222').catch(err => {
console.error('start failed', err)
})
client.handle('authenticate', authenticate => {
return authenticate('gabbar', 'gabbar#123')
})
sample.html
<head>
<meta charset="UTF-8"/>
<title>xmpp.js example</title>
<script src="node_modules/xmpp.js/dist/xmpp.min.js"></script>
<script src="index.js"></script>
</head>
these are the two ways I tried connecting to openfire from the browser side but none of them worked for me. please, can anybody tell me what I am doing wrong or any other possible better way of doing this?
xmpp:// is not supported in the browser. Only ws:// (websockets) is supported in browser. If the server supports websockets, you would do something like:
client.start('ws://domain:port) or client.start('ws://domain:port/xmpp-websockets)
The other option is to use Node not in a browser. Which would be accomplished by running node on it's own without a browser or running that code in the background process of Electron (same as just running node by itself, but you can communicate with the renderer process to interact with a UI)

Electron together with live mode in Angular isn't loading node_module resources

Context
I'm writing an application with Electron and Angular 2+ using Angular CLI. I've set up my electron .js file to point to the URL provided by the ng serve command, that usually is localhost:4200, in order to capture the code changes. Some considerations:
The address localhost:4200 points to index.html;
index.js is my Electron entry point script
Here is my index.js file used as an entry point for the electron module.
const {app, BrowserWindow} = require('electron');
const url = require('url');
const path = require('path');
let win = null;
function createWindow() {
win = new BrowserWindow({width: 1000, height: 600, show: false});
win.loadURL('http://localhost:4200');
win.maximize();
win.on('closed', () => {
win = null;
});
win.on('ready-to-show', () => {
win.show();
});
win.webContents.openDevTools();
}
app.on('ready', () => {
createWindow();
});
app.on('activate', () => {
if (win === null) {
createWindow();
}
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
And my index.html file:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>BRISA Carbon</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<!--Clarity Design Elements-->
<link rel="stylesheet" href="../node_modules/clarity-icons/clarity-icons.min.css">
<script type="text/javascript" src="../node_modules/#webcomponents/custom-elements/custom-elements.min.js"></script>
<script type="text/javascript" src="../node_modules/clarity-icons/clarity-icons.min.js"></script>
</head>
<body>
<app-root></app-root>
</body>
</html>
My Problem
When I run ng serve, the node_modules resources inside the .html file are not being loaded and the Chrome console outputs the following messages
I know that the path to the resources are correct because I'm using WebStorm IDE and I can go to the referenced element through a link like this image below.
Why my resources are not being loaded when I'm running in Angular live mode?
For everyone that is having this same problem, I just found a solution. Instead of loading my resources through the index.html file, I've placed them in the .angular-cli.json. Basically, Angular 2+ has the own way of importing resources and seems that is not correct loading resources from the main .html file.
Well, for scripts (I mean, .js files), I'm placing it in the scripts array and styles inside the styles array. The .angular-cli.json file section that I've changed looks like this:
"styles": [
"styles.css",
"../node_modules/clarity-icons/clarity-icons.min.css",
"../node_modules/clarity-ui/clarity-ui.min.css"
],
"scripts": [
"../node_modules/#webcomponents/custom-elements/custom-elements.min.js",
"../node_modules/clarity-icons/clarity-icons.min.js"
]
Hope that this information will help someone else. For me everything is working just fine.
I assume you're experiencing the same problem when you're visiting localhost:4200 in your browser also?
In your index.html you're referencing to ../node_modules/ ... which works in your IDE because that's how the folder structure looks like.
Let's say your folder structure looks like this:
node_modules/
src/
And src is the folder that your server's using as root. Then the server wont be able to retrieve files from .. (since a root dir has no parent dir).
Instead of referencing to the JS files in your index.html, you should import/require them in your other JS files. Exactly how you do that depnds on your building/bundling tools (eg Webpack and/or Babel).
import '#webcomponents/custom-elements/custom-elements.min.js'
import 'clarity-icons/clarity-icons.min.js'
import 'clarity-icons/clarity-icons.min.css' // Assumes that you use webpack and webpack-css-loader, for example

electron browser javascript error

I am very new to node, javascript, and electron. I am just trying to write a simple app that opens a local HTML file in a browser window. The local file has some complex embedded javascript (tiddlywiki). Here is some sample code (I did not use local file in this one, but the result is the same):
const {app, BrowserWindow} = require('electron')
const path = require('path')
const url = require('url')
let win
function createWindow () {
// Create the browser window.
win = new BrowserWindow({width: 800, height: 600})
// and load the index.html of the app.
win.loadURL(url.format({
pathname: 'tiddlywiki.com',
protocol: 'http:',
slashes: true,
webPreferences: {
nodeIntegration: false,
}
}))
When the electron app launches I get the following error in the browser dev tools.
Uncaught TypeError: Cannot read property 'length' of undefined
at Object.$tw.boot.startup (tiddlywiki.com/:27506)
at tiddlywiki.com/:27765
at Object.$tw.boot.decryptEncryptedTiddlers (tiddlywiki.com/:27053)
at Object.$tw.boot.boot (tiddlywiki.com/:27763)
at _boot (tiddlywiki.com/:27772)
at tiddlywiki.com/:27782
I assume this is because of some integration of the node.js object model? Sorry for the lack of understanding. Thanks in advance for the help.
You put the webPreferences into the wrong place.
You have to put it in the initialization for the BrowserWindow and not in url.format:
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false
}
})
You are on the right track. Another way you could do it is set nodeIntegration to false and preload a js file, which will get run in the BrowserWindow context and be able to access the window object once the process loaded event fires. The preload javascript file has full node integration just for itself.
I used it to make a TiddlyFox handler so I could use the TiddlyFox saver that comes in TiddlyWiki in my Electron app. Here is the code for it. It is actually very simple.
https://github.com/Arlen22/TiddlyWiki-Electron
If you want to load a TiddlyWiki datafolder directly into Electron, you can try loading this HTML file. Node Integration needs to be set to true in new BrowserWindow(...)
<!doctype html>
<html>
<head></head>
<body class="tc-body">
<script>
global.$tw = global.require("./boot/bootprefix.js").bootprefix();
global.$tw.boot.argv = ['./editions/server'];
global.$tw = require("./boot/boot.js").TiddlyWiki(global.$tw);
</script>
</body>
</html>
The way I ended up solving this was to use the following code in main.js:
win.loadURL(url.format({
pathname: path.join(__dirname, 'index2.html'),
protocol: 'file:',
slashes: true
}))
then index2 contains the following:
<html>
<body>
<webview id="webView" src="http://tiddlywiki.com" style="display:inline-flex; width:100%; height:100%" </webview>
</body>
</html>
Also tested with a local file:empty.html from tiddlywiki.com and that worked OK. I think this gives me the ability to preLoad a .js file to control some of the tiddlywiki events. Will have to learn more to test that out.

Categories