I'm sure there must be something simple I am missing with electron-builder / Sqiurrel.Windows; when
I open the built setup.exe file, nothing happens other than an animated splash screen. (I have
no experience of electron.builder or Squirrel.Windows, but I've been googling all I can).
I'm using Electron 1.4.2, electron-builder 7.10.2, using a two-package.json approach. The app being
built is a Koa app.
There is a very simple app which illustrates the problem on github.com/chrisveness/electron-koa-app.
My main.js is:
const { app, BrowserWindow } = require('electron'); // www.npmjs.com/package/electron
const spawn = require('child_process').spawn; // nodejs.org/api/child_process.html
const path = require('path'); // nodejs.org/api/path.html
let win = null; // keep global reference to window object to avoid automatic closing on JS GC
function createWindow() {
if (handleSquirrelCommand()) return;
console.log('createWindow');
app.server = require('./app/app.js'); // instantiate Koa app
win = new BrowserWindow({ width: 1024, height: 768 }); // create browser window
win.loadURL('http://localhost:3001'); // load koa-app home page
win.on('closed', () => { win = null; }); // dereference window object
}
app.on('ready', createWindow); // create window after Electron initialisation complete
app.on('window-all-closed', () => { // quit when all windows are closed
if (process.platform != 'darwin') app.quit(); // (except leave MacOS app active until Cmd+Q)
});
app.on('activate', () => { // re-recreate window when dock icon is clicked and no other windows open
if (win == null) createWindow();
});
// qv www.npmjs.com/package/electron-windows-installer
function handleSquirrelCommand() {
if (process.platform != 'win32') return false; // only applies to Windows (win32 is both 32- & 64-bit)
const command = process.argv[1];
const target = path.basename(process.execPath);
switch (command) {
case '--squirrel-install':
case '--squirrel-updated':
update(['--createShortcut=' + target + ''], app.quit);
return true;
case '--squirrel-uninstall':
update(['--removeShortcut=' + target + ''], app.quit);
return true;
case '--squirrel-obsolete':
app.quit();
return true;
}
return false;
}
function update(args, done) {
const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe');
spawn(updateExe, args, { detached: true }).on('close', done);
}
It may be clear what's missing just from reviewing the code, but otherwise, to replicate,
git clone https://github.com/chrisveness/electron-koa-app.git
cd electron-koa-app
npm install
Then running
npm start
opens the app perfectly well in Electron; but if I build a distributable (on Windows) with
npm run dist
then when I open the generated dist\win\koa-hello-world Setup 0.0.0.exe, nothing happens after the
animated splash screen.
Also, if I open %LocalAppData%\koahelloworld\app-0.0.0\koahelloworld.exe, I get an EADDRINUSE
error, which doesn't happen when I run the app using npm start.
What am I missing?
The main issue is that you are missing the entry point for electron into your app when it's running as a standalone build. The reason for this is that your main.js, which represents the main process, resides outside of the app directory. Only the sources in that directory are packaged unless you have a process/script in place that "assembles" all necessary sources from other locations and puts them together for the packager. The start script in the root package.json tells electron where the entrypoint is and that's why it works with npm start and not when packaged: "start": "electron main.js",.
So to fix this, you need to make the following changes:
package.json the start script to: "start": "electron app/main.js",main.js to the app/ folderapp/package.json to: "main": "main.js",if (handleSquirrelCommand()) return; to the top of your main script. app.js in the createWindow function: app.server = require('./app.js');scripts property from app/package.json. There are no scripts to run when your app is running. Your app is just launched based on what you put into the main property.I hope this is helpful and helps you get started with electron-builder. They've done an excellent job so far!
Also, consider to use NSIS target instead of Squirrel.Windows.
Thanks so much for that. I was definitely mixed up about the location for main.js, but that makes sense.
I still don't understand a return statement when there's no function to return from, but it works!
I hadn't even discovered app.makeSingleInstance(), that's really good.
So it all looks good now, thanks.
I was trying to look into the differences between NSIS and Squirrel.Windows, and which might be preferred, but got very lost, and assumed the default should be the best option.
Could you point me to something which gives clear info on the pros & cons? Would I put "target": "nsis" in the build section of the development package.json?
Yes, you'd add "target": ["nsis"] to the win build section.
In regards to Squirrel vs. NSIS: There seem to be a few issues with Squirrel and to paraphrase @develar: "I'm tired fixing it"
See: https://github.com/szwacz/electron-boilerplate/issues/172#issuecomment-232618357
and the referenced issue https://github.com/electron-userland/electron-builder/issues/472#issuecomment-223892438
For me a good cross-platform auto-updating functionality is more important than the actual installer. So, I'm really excited about the NSIS auto-updater (issue #529) functionality and how that will work across Mac and Windows at least. (EDIT: The electron-builder autoUpdater API I mean. I know NSIS is a Windows installer)
Thanks for that guidance @eriedl. My 'hello world' trial is working, though I feel there's quite a bit of (little documented) boilerplate in main.js, and I'm not quite sure how much of it is squirrel-specific. Once I can pause for breath, I may put together another issue on the subject to reach out for a bit more advice :)
Most helpful comment
The main issue is that you are missing the entry point for electron into your app when it's running as a standalone build. The reason for this is that your
main.js, which represents the main process, resides outside of theappdirectory. Only the sources in that directory are packaged unless you have a process/script in place that "assembles" all necessary sources from other locations and puts them together for the packager. The start script in the rootpackage.jsontells electron where the entrypoint is and that's why it works withnpm startand not when packaged:"start": "electron main.js",.So to fix this, you need to make the following changes:
package.jsonthe start script to:"start": "electron app/main.js",main.jsto theapp/folderapp/package.jsonto: "main": "main.js",if (handleSquirrelCommand()) return;to the top of your main script.app.jsin thecreateWindowfunction:app.server = require('./app.js');scriptsproperty fromapp/package.json. There are no scripts to run when your app is running. Your app is just launched based on what you put into themainproperty.I hope this is helpful and helps you get started with electron-builder. They've done an excellent job so far!