diff --git a/src/index.ts b/src/index.ts index de9b964..8053b4c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,8 +19,6 @@ import './main/print-raw-tcp'; import './main/serial-printer'; import './main/usb-printer'; import './main/printer-discovery'; -import './main/basePath'; -import './main/appVersion'; import './main/open-external-url'; // enabled logging when in development diff --git a/src/main/appVersion.ts b/src/main/appVersion.ts deleted file mode 100644 index f660962..0000000 --- a/src/main/appVersion.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { app, ipcMain } from 'electron'; - -/** - * This is used to get the app version. - * The version comes from package.json and is read by Electron's app module. - */ -ipcMain.on('getAppVersionSync', (event) => { - event.returnValue = app.getVersion(); -}); diff --git a/src/main/basePath.ts b/src/main/basePath.ts deleted file mode 100644 index fb6bca8..0000000 --- a/src/main/basePath.ts +++ /dev/null @@ -1,13 +0,0 @@ -import path from 'path'; - -import { ipcMain } from 'electron'; - -/** - * This is used to get the base path of the app. - * - * @NOTE - I'm leaving off the trailing slash, because expo has the slash at the start - */ -ipcMain.on('getBasePathSync', (event) => { - const basePath = `file://${path.join(process.resourcesPath, 'dist')}`; - event.returnValue = basePath; // Synchronously return the basePath -}); diff --git a/src/main/window.ts b/src/main/window.ts index 8fb000c..36e7174 100644 --- a/src/main/window.ts +++ b/src/main/window.ts @@ -1,11 +1,14 @@ import * as path from 'path'; -import { BrowserWindow, shell } from 'electron'; +import { app, BrowserWindow, shell } from 'electron'; import serve from 'electron-serve'; import { logger as log } from './log'; import { isDevelopment } from './util'; +// Keep in sync with src/preload.ts. +const APP_VERSION_ARG_PREFIX = '--wcpos-app-version='; + // Set up electron-serve let loadURL: (window: BrowserWindow) => void; @@ -35,6 +38,7 @@ export const createWindow = (): void => { sandbox: false, // Required for preload script to work nodeIntegration: false, // Prevent Node.js integration for security reasons contextIsolation: true, // Protect against prototype pollution + additionalArguments: [`${APP_VERSION_ARG_PREFIX}${app.getVersion()}`], }, backgroundColor: '#fff', }); diff --git a/src/preload.test.ts b/src/preload.test.ts index 0133fa8..6d2f818 100644 --- a/src/preload.test.ts +++ b/src/preload.test.ts @@ -1,5 +1,6 @@ import assert from 'node:assert/strict'; import Module from 'node:module'; +import path from 'node:path'; const exposures: Record = {}; const onCalls: { @@ -12,6 +13,8 @@ const removeListenerCalls: { channel: string; listener: (...args: unknown[]) => void; }[] = []; +const APP_VERSION_ARG_PREFIX = '--wcpos-app-version='; +const mockResourcesPath = path.join('/mock', 'resources'); const electronMock = { contextBridge: { @@ -21,12 +24,6 @@ const electronMock = { }, ipcRenderer: { sendSync(channel: string) { - if (channel === 'getBasePathSync') { - return '/mock-base-path'; - } - if (channel === 'getAppVersionSync') { - return '0.0.0-test'; - } throw new Error(`Unexpected sendSync channel: ${channel}`); }, send() {}, @@ -77,12 +74,30 @@ async function waitFor(condition: () => boolean, message: string) { async function main() { try { + Object.defineProperty(process, 'resourcesPath', { + value: mockResourcesPath, + configurable: true, + }); + process.argv.push(`${APP_VERSION_ARG_PREFIX}0.0.0-test`); // eslint-disable-next-line @typescript-eslint/no-require-imports require('./preload'); } finally { mutableModule._load = originalLoad; } + const exposedElectron = exposures.electron; + assert.ok(exposedElectron, 'preload should expose window.electron'); + assert.equal( + exposedElectron.basePath, + `file://${path.join(mockResourcesPath, 'dist')}`, + 'preload should expose the resources dist base path without a trailing slash' + ); + assert.equal( + exposedElectron.version, + '0.0.0-test', + 'preload should expose the app version from Electron additionalArguments' + ); + const exposedIpcRenderer = exposures.ipcRenderer; assert.ok(exposedIpcRenderer, 'preload should expose window.ipcRenderer'); assert.equal( diff --git a/src/preload.ts b/src/preload.ts index 5b308db..f9e5ae4 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -1,3 +1,5 @@ +import path from 'path'; + import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron'; import { @@ -7,16 +9,23 @@ import { serializeRxdbIpcMessage, } from './rxdb-ipc-attachments'; +// Keep in sync with src/main/window.ts. +const APP_VERSION_ARG_PREFIX = '--wcpos-app-version='; +const versionArg = process.argv.find((arg) => arg.startsWith(APP_VERSION_ARG_PREFIX)); +const version = versionArg ? versionArg.slice(APP_VERSION_ARG_PREFIX.length) : '0.0.0'; + +// No trailing slash: the Expo bundle puts the slash at the start of split asset paths. +const basePath = `file://${path.join(process.resourcesPath, 'dist')}`; + /** * Expose app info to the renderer process. * - * @NOTE - These are synchronous calls, they will block the thread, but they're quick calls. * basePath is needed for the bundle splitting to work correctly. * version is needed for app-info utility to report correct electron version. */ contextBridge.exposeInMainWorld('electron', { - basePath: ipcRenderer.sendSync('getBasePathSync'), - version: ipcRenderer.sendSync('getAppVersionSync'), + basePath, + version, }); // Must match IPC_RENDERER_KEY_PREFIX from 'rxdb/plugins/electron' used in src/main/rxdb-storage.ts