Move Electron app entrypoint, handle launch errors

This commit is contained in:
Samuel Elliott 2022-09-20 15:50:11 +01:00
parent 13486a89c8
commit fa2854b527
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
9 changed files with 52 additions and 38 deletions

2
.vscode/launch.json vendored
View File

@ -17,7 +17,7 @@
"runtimeExecutable": "${workspaceFolder}/node_modules/electron/dist/electron.exe"
},
"args": [
"dist/app/main/app-entry.cjs"
"dist/app/app-entry.cjs"
],
"outputCapture": "std",
"env": {

View File

@ -111,7 +111,7 @@ const main = {
* @type {import('rollup').RollupOptions}
*/
const app_entry = {
input: 'src/app/main/app-entry.cts',
input: 'src/app/app-entry.cts',
output: {
file: 'dist/bundle/app-entry.cjs',
format: 'iife',
@ -121,7 +121,7 @@ const app_entry = {
plugins: [
replace(replace_options),
replace({
include: ['src/app/main/app-entry.cts'],
include: ['src/app/app-entry.cts'],
values: {
'__NXAPI_BUNDLE_APP_MAIN__': JSON.stringify('./app-main-bundle.js'),
},
@ -147,7 +147,7 @@ const app_entry = {
],
external: [
'electron',
path.resolve(__dirname, 'src/app/main/app-main-bundle.js'),
path.resolve(__dirname, 'src/app/app-main-bundle.js'),
],
watch,
};

View File

@ -3,7 +3,7 @@ Electron app
The Electron app is bundled into ~4 files in `dist/app/bundle` using Rollup. The main process code is not bundled for development, but is when packaging the app at `dist/bundle` (with the command line executable at `dist/bundle/cli-bundle.js`).
[electron.ts](electron.ts) exports all Electron APIs used in the main process. This is because the `electron` module doesn't actually exist - Electron patches the `require` function (but not the module importer). Additionally Electron does not support using a standard JavaScript module as the app entrypoint, so [main/app-entry.cts](main/app-entry.cts) (a CommonJS module) is used to import the actual app entrypoint after the `ready` event.
[electron.ts](electron.ts) exports all Electron APIs used in the main process. This is because the `electron` module doesn't actually exist - Electron patches the `require` function (but not the module importer). Additionally Electron does not support using a standard JavaScript module as the app entrypoint, so [app-entry.cts](app-entry.cts) (a CommonJS module) is used to import the actual app entrypoint after the `ready` event.
Electron APIs used in renderer processes should be imported directly from the `electron` module as they are always bundled into a CommonJS module.

12
src/app/app-entry.cts Normal file
View File

@ -0,0 +1,12 @@
const electron = require('electron');
// Do anything that must be run before the app is ready...
electron.app.whenReady()
// @ts-expect-error
.then(() => typeof __NXAPI_BUNDLE_APP_MAIN__ !== 'undefined' ? import(__NXAPI_BUNDLE_APP_MAIN__) : import('./main/index.js'))
.then(m => m.init.call(null))
.catch(err => {
electron.dialog.showErrorBox('Error during startup', err?.stack ?? err?.message ?? err);
process.exit(1);
});

View File

@ -1,8 +0,0 @@
const electron = require('electron');
// Do anything that must be run before the app is ready...
electron.app.whenReady()
// @ts-expect-error
.then(() => typeof __NXAPI_BUNDLE_APP_MAIN__ !== 'undefined' ? import(__NXAPI_BUNDLE_APP_MAIN__) : import('./index.js'))
.then(m => m.init.call(null));

View File

@ -4,8 +4,7 @@ import * as path from 'node:path';
import { EventEmitter } from 'node:events';
import createDebug from 'debug';
import * as persist from 'node-persist';
import dotenv from 'dotenv';
import dotenvExpand from 'dotenv-expand';
import { init as initGlobals } from '../../common/globals.js';
import MenuApp from './menu.js';
import { handleOpenWebServiceUri } from './webservices.js';
import { EmbeddedPresenceMonitor, PresenceMonitorManager } from './monitor.js';
@ -26,7 +25,7 @@ const debug = createDebug('app:main');
export const protocol_registration_options = dev && process.platform === 'win32' ? {
path: process.execPath,
argv: [
path.join(dir, 'dist', 'app', 'main', 'app-entry.cjs'),
path.join(dir, 'dist', 'app', 'app-entry.cjs'),
],
} : null;
export const login_item_options: LoginItemSettingsOptions = {};
@ -107,15 +106,7 @@ export async function init() {
return;
}
dotenvExpand.expand(dotenv.config({
path: path.join(paths.data, '.env'),
}));
if (process.env.NXAPI_DATA_PATH) dotenvExpand.expand(dotenv.config({
path: path.join(process.env.NXAPI_DATA_PATH, '.env'),
}));
if (process.env.DEBUG) createDebug.enable(process.env.DEBUG);
initGlobals();
addUserAgent('nxapi-app (Chromium ' + process.versions.chrome + '; Electron ' + process.versions.electron + ')');
const storage = await initStorage(process.env.NXAPI_DATA_PATH ?? paths.data);
@ -145,6 +136,8 @@ export async function init() {
app.on('open-url', (event, url) => {
debug('Open URL', url);
event.preventDefault();
if (!tryHandleUrl(appinstance, url)) {
appinstance.showMainWindow();
}
@ -153,7 +146,9 @@ export async function init() {
app.setAsDefaultProtocolClient('com.nintendo.znca',
protocol_registration_options?.path, protocol_registration_options?.argv);
app.on('activate', () => {
app.on('activate', (event, has_visible_windows) => {
debug('activate', has_visible_windows);
if (BrowserWindow.getAllWindows().length === 0) appinstance.showMainWindow();
});

View File

@ -1,9 +1,6 @@
import process from 'node:process';
import * as path from 'node:path';
import createDebug from 'debug';
import Yargs from 'yargs';
import dotenv from 'dotenv';
import dotenvExpand from 'dotenv-expand';
import * as commands from './cli/index.js';
import { checkUpdates } from './common/update.js';
import { dev } from './util/product.js';
@ -11,17 +8,11 @@ import { paths } from './util/storage.js';
import { YargsArguments } from './util/yargs.js';
import { addUserAgent } from './util/useragent.js';
import { USER_AGENT_INFO_URL } from './common/constants.js';
import { init as initGlobals } from './common/globals.js';
const debug = createDebug('cli');
dotenvExpand.expand(dotenv.config({
path: path.join(paths.data, '.env'),
}));
if (process.env.NXAPI_DATA_PATH) dotenvExpand.expand(dotenv.config({
path: path.join(process.env.NXAPI_DATA_PATH, '.env'),
}));
if (process.env.DEBUG) createDebug.enable(process.env.DEBUG);
initGlobals();
export function createYargs(argv: string[]) {
const yargs = Yargs(argv).option('data-path', {

View File

@ -25,7 +25,7 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
}
execFileSync(electron, [
'dist/app/main/app-entry.cjs',
'dist/app/app-entry.cjs',
], {
stdio: 'inherit',
env: {

24
src/common/globals.ts Normal file
View File

@ -0,0 +1,24 @@
import * as path from 'node:path';
import createDebug from 'debug';
import dotenv from 'dotenv';
import dotenvExpand from 'dotenv-expand';
import { paths } from '../util/storage.js';
let done = false;
export function init() {
if (done) {
throw new Error('Attempted to initialise global data twice');
}
done = true;
dotenvExpand.expand(dotenv.config({
path: path.join(paths.data, '.env'),
}));
if (process.env.NXAPI_DATA_PATH) dotenvExpand.expand(dotenv.config({
path: path.join(process.env.NXAPI_DATA_PATH, '.env'),
}));
if (process.env.DEBUG) createDebug.enable(process.env.DEBUG);
}