I'm new to electron and trying to use custom protocols for my application and distributed client-side js files, that are require
d. However, I'm not able to load the modules, because require
is ignoring my custom scheme. This is my environment:
lib/ - node scripts
main.js - node entry script
resources/ - client side resources
css/
html/
index.html
js/ - client side scripts
main.js
test.js
package.json
lib/main.js is based on the example from the public homepage:
// lib/main.js
'use strict';
const electron = require('electron');
const app = electron.app; // Module to control application life.
const BrowserWindow = electron.BrowserWindow; // Module to create native browser window.
const path = require('path');
var mainWindow = null;
app.on('ready', function() {
const protocol = electron.protocol;
/* js, css, fonts, ... */
protocol.registerFileProtocol('resource', (request, callback) => {
var url = request.url.substr(11);
callback({path: path.join(__dirname, '../resources', url)});
});
/* html, possible rendered from other files like markdown or jade */
protocol.registerFileProtocol('view', (request, callback) => {
var url = request.url.substr(11);
callback({path: path.join(__dirname, '../resources/html', url)});
});
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600});
// and load the index.html of the app.
mainWindow.loadURL('view://index.html');
// Open the DevTools.
mainWindow.webContents.openDevTools();
// ...
});
Note that the process behind the view://
protocol is going to compile other files into html in the future, such as markdown or jade (view://index.jade
will compile the jade file and return html string).
<!-- resources/html/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Electron Test</title>
</head>
<body>
<h1>Electron Test</h1>
<script src="resource://js/main.js"></script>
</body>
</html>
The html file will access resources like css or js files via my custom resource://
protocol. The main file is executed correctly, however I can't require
another file there:
// resources/js/main.js
const test = require('./test'); // Error: Cannot find module './test'
test.sayHello();
// resources/js/test.js
exports.sayHello = function() {
console.log('Hello!');
};
I'm executing it with electron .
, v0.36.7.
When inspecting the code behind require
in the devtools (around module.js:336
, line 337 in node's current master) when the error occurs, request
is "./test"
and paths
only contains one entry for electron's core modules, and I couldn't find a way to extend that list of search paths.
I've checked other issues (#4459, #2539) that seem to be about the same issue, but they didn't help me (e.g. using a preload script manipulating module
's path or globalPaths does not work.
Any ideas how to get this working? Or would you recommend using a single JavaScript file? (I am using TypeScript as a preprocessor, that can concatenate all source files).
You can check the search paths of Node modules in module.paths
, if your resources/js
is not in it, then require
won't be able to find test.js
.
I think it is because you didn't manipulate the module search paths correctly, you might be a bee to get some ideas in the community.
When you load a .js
file through a <script>
tag it's embedded in the document, so
<body>
<h1>Electron Test</h1>
<script src="resource://js/main.js"></script>
</body>
is functionally equivalent to
<body>
<h1>Electron Test</h1>
<script>
const test = require('./test'); // Error: Cannot find module './test'
test.sayHello();
</script>
</body>
Which means that ./test
is resolved relative to index.html
, not relative to js/main.js
. To fix this you can do
<body>
<h1>Electron Test</h1>
<script>
require('../js/main.js');
</script>
</body>
I found this solution for whomever it may concern:
The WEB_FOLDER contains the index.html and all other relative referenced content.
You can set it to any empty string if your index file isn't located inside a sub folder (web in this example)
function createWindow() {
const WEB_FOLDER = 'web';
const PROTOCOL = 'file';
electron.protocol.interceptFileProtocol(PROTOCOL, (request, callback) => {
// // Strip protocol
let url = request.url.substr(PROTOCOL.length + 1);
// Build complete path for node require function
url = path.join(__dirname, WEB_FOLDER, url);
// Replace backslashes by forward slashes (windows)
// url = url.replace(/\\/g, '/');
url = path.normalize(url);
console.log(url);
callback({path: url});
});
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false
}
});
// and load the index.html of the app.
mainWindow.loadURL(url.format({
pathname: 'index.html',
protocol: PROTOCOL + ':',
slashes: true
}));
Most helpful comment
I found this solution for whomever it may concern:
The WEB_FOLDER contains the index.html and all other relative referenced content.
You can set it to any empty string if your index file isn't located inside a sub folder (web in this example)