From 852c3b0eaed48265354046d068f0cfa565827e7c Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Thu, 27 Jan 2022 16:49:11 +0100 Subject: refactor: Extract resource path management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lets us access absolute paths and URLs without directly calling node APIs. Signed-off-by: Kristóf Marussy --- .../src/infrastructure/electron/impl/devTools.ts | 94 +++++++++++++++++ .../main/src/infrastructure/resources/Resources.ts | 27 +++++ .../impl/__tests__/getDistResources.spec.ts | 112 +++++++++++++++++++++ .../resources/impl/getDistResources.ts | 59 +++++++++++ 4 files changed, 292 insertions(+) create mode 100644 packages/main/src/infrastructure/electron/impl/devTools.ts create mode 100644 packages/main/src/infrastructure/resources/Resources.ts create mode 100644 packages/main/src/infrastructure/resources/impl/__tests__/getDistResources.spec.ts create mode 100644 packages/main/src/infrastructure/resources/impl/getDistResources.ts (limited to 'packages/main/src/infrastructure') diff --git a/packages/main/src/infrastructure/electron/impl/devTools.ts b/packages/main/src/infrastructure/electron/impl/devTools.ts new file mode 100644 index 0000000..10f4545 --- /dev/null +++ b/packages/main/src/infrastructure/electron/impl/devTools.ts @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2021-2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import type { BrowserWindow } from 'electron'; + +/** + * URL prefixes Sophie is allowed load in dev mode. + * + * In dev mode, in addition to the application itself, + * Sophie must be able do download and load the devtools and related extensions, + * so we have to make exceptions in the UI process request filter. + */ +export const DEVMODE_ALLOWED_URL_PREFIXES = [ + 'chrome-extension:', + 'devtools:', + 'https://clients2.google.com/service/update2/crx', + 'https://clients2.googleusercontent.com/crx', +]; + +/** + * Enables using source maps for node stack traces. + */ +export function enableStacktraceSourceMaps(): void { + const sourceMapSupport = + /* eslint-disable-next-line + import/no-extraneous-dependencies, + global-require, + @typescript-eslint/no-var-requires, + unicorn/prefer-module -- + Hack to lazily require a CJS module from an ES module transpiled into a CJS module. + */ + require('source-map-support') as typeof import('source-map-support'); + sourceMapSupport.install(); +} + +/** + * Installs the react and redux developer tools extensions. + * + * We use the redux devtools and connect the mobx store to it with `mst-middlewares`, + * because the mobx-state-tree devtools are currently unmaintained. + */ +export async function installDevToolsExtensions(): Promise { + const { + default: installExtension, + REACT_DEVELOPER_TOOLS, + REDUX_DEVTOOLS, + /* eslint-disable-next-line + import/no-extraneous-dependencies, + global-require, + @typescript-eslint/no-var-requires, + unicorn/prefer-module -- + Hack to lazily require a CJS module from an ES module transpiled into a CJS module. + */ + } = require('electron-devtools-installer') as typeof import('electron-devtools-installer'); + await installExtension([REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS], { + forceDownload: false, + loadExtensionOptions: { + allowFileAccess: true, + }, + }); +} + +/** + * Opens the developer tools while applying a workaround to enable the redux devtools. + * + * @param browserWindow The browser window to open the devtools in. + * @see https://github.com/MarshallOfSound/electron-devtools-installer/issues/195#issuecomment-998872878 + */ +export function openDevToolsWhenReady(browserWindow: BrowserWindow): void { + const { webContents } = browserWindow; + webContents.once('dom-ready', () => { + webContents.once('devtools-opened', () => { + browserWindow?.focus(); + }); + webContents.openDevTools(); + }); +} diff --git a/packages/main/src/infrastructure/resources/Resources.ts b/packages/main/src/infrastructure/resources/Resources.ts new file mode 100644 index 0000000..269c838 --- /dev/null +++ b/packages/main/src/infrastructure/resources/Resources.ts @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export default interface Resources { + getPath(packageName: string, relativePathInPackage: string): string; + + getFileURL(packageName: string, relativePathInPackage: string): string; + + getRendererURL(relativePathInRendererPackage: string): string; +} diff --git a/packages/main/src/infrastructure/resources/impl/__tests__/getDistResources.spec.ts b/packages/main/src/infrastructure/resources/impl/__tests__/getDistResources.spec.ts new file mode 100644 index 0000000..d045e54 --- /dev/null +++ b/packages/main/src/infrastructure/resources/impl/__tests__/getDistResources.spec.ts @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import os from 'node:os'; + +import eachModule from 'jest-each'; + +import Resources from '../../Resources'; +import getDistResources from '../getDistResources'; + +// Workaround for jest ESM loader incorrectly wrapping the import in another layer of `default`. +const each = + (eachModule as Partial).default ?? eachModule; + +const defaultDevServerURL = 'http://localhost:3000/'; + +const [ + thisDir, + preloadIndexPath, + preloadIndexFileURL, + rendererIndexFileURL, + rendererRootFileURL, +] = + os.platform() === 'win32' + ? [ + 'C:\\Program Files\\sophie\\resources\\app.asar\\main\\dist', + 'C:\\Program Files\\sophie\\resources\\app.asar\\preload\\dist\\index.cjs', + 'file:///C:/Program Files/sophie/resources/app.asar/preload/dist/index.cjs', + 'file:///C:/Program Files/sophie/resources/app.asar/renderer/dist/index.html', + 'file:///C:/Program Files/sophie/resources/app.asar/renderer/dist/', + ] + : [ + '/opt/sophie/resources/app.asar/main/dist', + '/opt/sophie/resources/app.asar/preload/dist/index.cjs', + 'file:///opt/sophie/resources/app.asar/preload/dist/index.cjs', + 'file:///opt/sophie/resources/app.asar/renderer/dist/index.html', + 'file:///opt/sophie/resources/app.asar/renderer/dist/', + ]; + +const fileURLs: [string, string] = [rendererIndexFileURL, rendererRootFileURL]; + +each([ + ['not in dev mode', false, undefined, ...fileURLs], + [ + 'not in dev mode with VITE_DEV_SERVER_URL set', + false, + defaultDevServerURL, + ...fileURLs, + ], + ['in dev mode with no VITE_DEV_SERVER_URL', true, undefined, ...fileURLs], + [ + 'in dev mode with VITE_DEV_SERVER_URL set', + true, + defaultDevServerURL, + `${defaultDevServerURL}index.html`, + defaultDevServerURL, + ], +]).describe( + 'when %s', + ( + _description: string, + devMode: boolean, + devServerURL: string, + rendererIndexURL: string, + rendererRootURL: string, + ) => { + let resources: Resources; + + beforeEach(() => { + resources = getDistResources(devMode, thisDir, devServerURL); + }); + + it('getPath should return the path to the requested resource', () => { + const path = resources.getPath('preload', 'index.cjs'); + expect(path).toBe(preloadIndexPath); + }); + + it('getFileURL should return the file URL to the requested resource', () => { + const url = resources.getFileURL('preload', 'index.cjs'); + expect(url).toBe(preloadIndexFileURL); + }); + + describe('getRendererURL', () => { + it('should return the URL to the requested resource', () => { + const url = resources.getRendererURL('index.html'); + expect(url).toBe(rendererIndexURL); + }); + + it('should return the root URL', () => { + const url = resources.getRendererURL('/'); + expect(url).toBe(rendererRootURL); + }); + }); + }, +); diff --git a/packages/main/src/infrastructure/resources/impl/getDistResources.ts b/packages/main/src/infrastructure/resources/impl/getDistResources.ts new file mode 100644 index 0000000..f3c3f7b --- /dev/null +++ b/packages/main/src/infrastructure/resources/impl/getDistResources.ts @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import path from 'node:path'; +import { pathToFileURL, URL } from 'node:url'; + +import Resources from '../Resources'; + +export default function getDistResources( + devMode: boolean, + /* + eslint-disable-next-line unicorn/prefer-module -- + Electron apps run in a commonjs environment, so there is no `import.meta.url`. + */ + thisDir = __dirname, + devServerURL = import.meta.env?.VITE_DEV_SERVER_URL, +): Resources { + const packagesRoot = path.join(thisDir, '..', '..'); + + function getPath(packageName: string, relativePathInPackage: string): string { + return path.join(packagesRoot, packageName, 'dist', relativePathInPackage); + } + + function getFileURL( + packageName: string, + relativePathInPackage: string, + ): string { + const absolutePath = getPath(packageName, relativePathInPackage); + return pathToFileURL(absolutePath).toString(); + } + + return { + getPath, + getFileURL, + getRendererURL: + devMode && devServerURL !== undefined + ? (relativePathInRendererPackage) => + new URL(relativePathInRendererPackage, devServerURL).toString() + : (relativePathInRendererPackage) => + getFileURL('renderer', relativePathInRendererPackage), + }; +} -- cgit v1.2.3-54-g00ecf