diff options
Diffstat (limited to 'packages')
23 files changed, 122 insertions, 107 deletions
diff --git a/packages/main/.eslintrc.cjs b/packages/main/.eslintrc.cjs index 548ea34..cab227c 100644 --- a/packages/main/.eslintrc.cjs +++ b/packages/main/.eslintrc.cjs | |||
@@ -3,4 +3,8 @@ module.exports = { | |||
3 | node: true, | 3 | node: true, |
4 | browser: false, | 4 | browser: false, |
5 | }, | 5 | }, |
6 | rules: { | ||
7 | // This is an application, so we're allowed to `exit` from it. | ||
8 | 'unicorn/no-process-exit': 'off', | ||
9 | }, | ||
6 | }; | 10 | }; |
diff --git a/packages/main/esbuild.config.js b/packages/main/esbuild.config.js index d5f6f1e..ef0aa71 100644 --- a/packages/main/esbuild.config.js +++ b/packages/main/esbuild.config.js | |||
@@ -1,7 +1,7 @@ | |||
1 | import getRepoInfo from 'git-repo-info'; | 1 | import getRepoInfo from 'git-repo-info'; |
2 | 2 | ||
3 | import { node } from '../../config/buildConstants.js'; | 3 | import { node } from '../../config/buildConstants.js'; |
4 | import fileURLToDirname from '../../config/fileURLToDirname.js'; | 4 | import fileUrlToDirname from '../../config/fileUrlToDirname.js'; |
5 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; | 5 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; |
6 | 6 | ||
7 | const externalPackages = ['electron']; | 7 | const externalPackages = ['electron']; |
@@ -14,7 +14,7 @@ const gitInfo = getRepoInfo(); | |||
14 | 14 | ||
15 | export default getEsbuildConfig( | 15 | export default getEsbuildConfig( |
16 | { | 16 | { |
17 | absWorkingDir: fileURLToDirname(import.meta.url), | 17 | absWorkingDir: fileUrlToDirname(import.meta.url), |
18 | entryPoints: ['src/index.ts'], | 18 | entryPoints: ['src/index.ts'], |
19 | outfile: 'dist/index.cjs', | 19 | outfile: 'dist/index.cjs', |
20 | format: 'cjs', | 20 | format: 'cjs', |
@@ -23,9 +23,9 @@ export default getEsbuildConfig( | |||
23 | external: externalPackages, | 23 | external: externalPackages, |
24 | }, | 24 | }, |
25 | { | 25 | { |
26 | VITE_DEV_SERVER_URL: process.env.VITE_DEV_SERVER_URL || null, | 26 | VITE_DEV_SERVER_URL: process.env.VITE_DEV_SERVER_URL || undefined, |
27 | GIT_SHA: gitInfo.abbreviatedSha, | 27 | GIT_SHA: gitInfo.abbreviatedSha, |
28 | GIT_BRANCH: gitInfo.branch, | 28 | GIT_BRANCH: gitInfo.branch, |
29 | BUILD_DATE: new Date().getTime(), | 29 | BUILD_DATE: Date.now(), |
30 | }, | 30 | }, |
31 | ); | 31 | ); |
diff --git a/packages/main/jest.config.js b/packages/main/jest.config.js index b86463c..9aaf344 100644 --- a/packages/main/jest.config.js +++ b/packages/main/jest.config.js | |||
@@ -1,3 +1,4 @@ | |||
1 | import rootConfig from '../../config/jest.config.base.js'; | 1 | import baseConfig from '../../config/jest.config.base.js'; |
2 | 2 | ||
3 | export default rootConfig; | 3 | // eslint-disable-next-line unicorn/prefer-export-from -- Can't export from default. |
4 | export default baseConfig; | ||
diff --git a/packages/main/src/controllers/__tests__/initConfig.spec.ts b/packages/main/src/controllers/__tests__/initConfig.spec.ts index 9a5f85e..11e7690 100644 --- a/packages/main/src/controllers/__tests__/initConfig.spec.ts +++ b/packages/main/src/controllers/__tests__/initConfig.spec.ts | |||
@@ -133,7 +133,7 @@ describe('when it has loaded the config', () => { | |||
133 | }); | 133 | }); |
134 | 134 | ||
135 | it('should throttle saving changes to the config file', () => { | 135 | it('should throttle saving changes to the config file', () => { |
136 | mocked(persistenceService.writeConfig).mockResolvedValue(undefined); | 136 | mocked(persistenceService.writeConfig).mockResolvedValue(); |
137 | config.setThemeSource('dark'); | 137 | config.setThemeSource('dark'); |
138 | jest.advanceTimersByTime(lessThanThrottleMs); | 138 | jest.advanceTimersByTime(lessThanThrottleMs); |
139 | config.setThemeSource('light'); | 139 | config.setThemeSource('light'); |
diff --git a/packages/main/src/controllers/initConfig.ts b/packages/main/src/controllers/initConfig.ts index e83b8da..915f451 100644 --- a/packages/main/src/controllers/initConfig.ts +++ b/packages/main/src/controllers/initConfig.ts | |||
@@ -38,7 +38,7 @@ export default async function initConfig( | |||
38 | ): Promise<Disposer> { | 38 | ): Promise<Disposer> { |
39 | log.trace('Initializing config controller'); | 39 | log.trace('Initializing config controller'); |
40 | 40 | ||
41 | let lastSnapshotOnDisk: ConfigSnapshotOut | null = null; | 41 | let lastSnapshotOnDisk: ConfigSnapshotOut | undefined; |
42 | 42 | ||
43 | async function readConfig(): Promise<boolean> { | 43 | async function readConfig(): Promise<boolean> { |
44 | const result = await persistenceService.readConfig(); | 44 | const result = await persistenceService.readConfig(); |
@@ -46,8 +46,8 @@ export default async function initConfig( | |||
46 | try { | 46 | try { |
47 | applySnapshot(config, result.data); | 47 | applySnapshot(config, result.data); |
48 | lastSnapshotOnDisk = getSnapshot(config); | 48 | lastSnapshotOnDisk = getSnapshot(config); |
49 | } catch (err) { | 49 | } catch (error) { |
50 | log.error('Failed to apply config snapshot', result.data, err); | 50 | log.error('Failed to apply config snapshot', result.data, error); |
51 | } | 51 | } |
52 | } | 52 | } |
53 | return result.found; | 53 | return result.found; |
@@ -70,8 +70,8 @@ export default async function initConfig( | |||
70 | debounce((snapshot) => { | 70 | debounce((snapshot) => { |
71 | // We can compare snapshots by reference, since it is only recreated on store changes. | 71 | // We can compare snapshots by reference, since it is only recreated on store changes. |
72 | if (lastSnapshotOnDisk !== snapshot) { | 72 | if (lastSnapshotOnDisk !== snapshot) { |
73 | writeConfig().catch((err) => { | 73 | writeConfig().catch((error) => { |
74 | log.error('Failed to write config on config change', err); | 74 | log.error('Failed to write config on config change', error); |
75 | }); | 75 | }); |
76 | } | 76 | } |
77 | }, debounceTime), | 77 | }, debounceTime), |
@@ -80,8 +80,8 @@ export default async function initConfig( | |||
80 | const disposeWatcher = persistenceService.watchConfig(async () => { | 80 | const disposeWatcher = persistenceService.watchConfig(async () => { |
81 | try { | 81 | try { |
82 | await readConfig(); | 82 | await readConfig(); |
83 | } catch (err) { | 83 | } catch (error) { |
84 | log.error('Failed to read config', err); | 84 | log.error('Failed to read config', error); |
85 | } | 85 | } |
86 | }, debounceTime); | 86 | }, debounceTime); |
87 | 87 | ||
diff --git a/packages/main/src/devTools.ts b/packages/main/src/devTools.ts index 69f1514..4ca1bf3 100644 --- a/packages/main/src/devTools.ts +++ b/packages/main/src/devTools.ts | |||
@@ -49,7 +49,8 @@ export async function installDevToolsExtensions(): Promise<void> { | |||
49 | /* eslint-disable-next-line | 49 | /* eslint-disable-next-line |
50 | import/no-extraneous-dependencies, | 50 | import/no-extraneous-dependencies, |
51 | global-require, | 51 | global-require, |
52 | @typescript-eslint/no-var-requires | 52 | @typescript-eslint/no-var-requires, |
53 | unicorn/prefer-module | ||
53 | */ | 54 | */ |
54 | } = require('electron-devtools-installer') as typeof import('electron-devtools-installer'); | 55 | } = require('electron-devtools-installer') as typeof import('electron-devtools-installer'); |
55 | await installExtension([REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS], { | 56 | await installExtension([REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS], { |
diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts index 1f80e44..02e6cda 100644 --- a/packages/main/src/index.ts +++ b/packages/main/src/index.ts | |||
@@ -19,9 +19,9 @@ | |||
19 | * SPDX-License-Identifier: AGPL-3.0-only | 19 | * SPDX-License-Identifier: AGPL-3.0-only |
20 | */ | 20 | */ |
21 | 21 | ||
22 | import { arch } from 'os'; | 22 | import { arch } from 'node:os'; |
23 | import { join } from 'path'; | 23 | import path from 'node:path'; |
24 | import { URL } from 'url'; | 24 | import { URL } from 'node:url'; |
25 | 25 | ||
26 | import { | 26 | import { |
27 | ServiceToMainIpcMessage, | 27 | ServiceToMainIpcMessage, |
@@ -101,11 +101,14 @@ app.setAboutPanelOptions({ | |||
101 | version: '', | 101 | version: '', |
102 | }); | 102 | }); |
103 | 103 | ||
104 | // eslint-disable-next-line unicorn/prefer-module -- Electron apps run in a commonjs environment. | ||
105 | const thisDir = __dirname; | ||
106 | |||
104 | function getResourcePath(relativePath: string): string { | 107 | function getResourcePath(relativePath: string): string { |
105 | return join(__dirname, relativePath); | 108 | return path.join(thisDir, relativePath); |
106 | } | 109 | } |
107 | 110 | ||
108 | const baseUrl = `file://${__dirname}`; | 111 | const baseUrl = `file://${thisDir}`; |
109 | function getResourceUrl(relativePath: string): string { | 112 | function getResourceUrl(relativePath: string): string { |
110 | return new URL(relativePath, baseUrl).toString(); | 113 | return new URL(relativePath, baseUrl).toString(); |
111 | } | 114 | } |
@@ -117,15 +120,15 @@ const serviceInject: WebSource = { | |||
117 | url: getResourceUrl(serviceInjectRelativePath), | 120 | url: getResourceUrl(serviceInjectRelativePath), |
118 | }; | 121 | }; |
119 | 122 | ||
120 | let mainWindow: BrowserWindow | null = null; | 123 | let mainWindow: BrowserWindow | undefined; |
121 | 124 | ||
122 | const store = createMainStore(); | 125 | const store = createMainStore(); |
123 | init(store) | 126 | init(store) |
124 | .then((disposeCompositionRoot) => { | 127 | .then((disposeCompositionRoot) => { |
125 | app.on('will-quit', disposeCompositionRoot); | 128 | app.on('will-quit', disposeCompositionRoot); |
126 | }) | 129 | }) |
127 | .catch((err) => { | 130 | .catch((error) => { |
128 | log.log('Failed to initialize application', err); | 131 | log.log('Failed to initialize application', error); |
129 | }); | 132 | }); |
130 | 133 | ||
131 | const rendererBaseUrl = getResourceUrl('../renderer/'); | 134 | const rendererBaseUrl = getResourceUrl('../renderer/'); |
@@ -136,7 +139,7 @@ function shouldCancelMainWindowRequest(url: string, method: string): boolean { | |||
136 | let normalizedUrl: string; | 139 | let normalizedUrl: string; |
137 | try { | 140 | try { |
138 | normalizedUrl = new URL(url).toString(); | 141 | normalizedUrl = new URL(url).toString(); |
139 | } catch (_err) { | 142 | } catch { |
140 | return true; | 143 | return true; |
141 | } | 144 | } |
142 | if (isDevelopment) { | 145 | if (isDevelopment) { |
@@ -233,7 +236,7 @@ async function createWindow(): Promise<unknown> { | |||
233 | 'from webContents', | 236 | 'from webContents', |
234 | event.sender.id, | 237 | event.sender.id, |
235 | ); | 238 | ); |
236 | return null; | 239 | throw new Error('Invalid IPC call'); |
237 | } | 240 | } |
238 | return getSnapshot(store.shared); | 241 | return getSnapshot(store.shared); |
239 | }); | 242 | }); |
@@ -262,22 +265,22 @@ async function createWindow(): Promise<unknown> { | |||
262 | .then((data) => { | 265 | .then((data) => { |
263 | serviceInject.code = data; | 266 | serviceInject.code = data; |
264 | }) | 267 | }) |
265 | .catch((err) => { | 268 | .catch((error) => { |
266 | log.error('Error while reloading', serviceInjectPath, err); | 269 | log.error('Error while reloading', serviceInjectPath, error); |
267 | }) | 270 | }) |
268 | .then(() => { | 271 | .then(() => { |
269 | browserView.webContents.reload(); | 272 | browserView.webContents.reload(); |
270 | }) | 273 | }) |
271 | .catch((err) => { | 274 | .catch((error) => { |
272 | log.error('Failed to reload browserView', err); | 275 | log.error('Failed to reload browserView', error); |
273 | }); | 276 | }); |
274 | break; | 277 | break; |
275 | default: | 278 | default: |
276 | log.error('Unexpected action from UI renderer:', actionToDispatch); | 279 | log.error('Unexpected action from UI renderer:', actionToDispatch); |
277 | break; | 280 | break; |
278 | } | 281 | } |
279 | } catch (err) { | 282 | } catch (error) { |
280 | log.error('Error while dispatching renderer action', rawAction, err); | 283 | log.error('Error while dispatching renderer action', rawAction, error); |
281 | } | 284 | } |
282 | }); | 285 | }); |
283 | 286 | ||
@@ -285,9 +288,18 @@ async function createWindow(): Promise<unknown> { | |||
285 | webContents.send(MainToRendererIpcMessage.SharedStorePatch, patch); | 288 | webContents.send(MainToRendererIpcMessage.SharedStorePatch, patch); |
286 | }); | 289 | }); |
287 | 290 | ||
288 | ipcMain.handle(ServiceToMainIpcMessage.ApiExposedInMainWorld, (event) => | 291 | ipcMain.handle(ServiceToMainIpcMessage.ApiExposedInMainWorld, (event) => { |
289 | event.sender.id === browserView.webContents.id ? serviceInject : null, | 292 | if (event.sender.id !== browserView.webContents.id) { |
290 | ); | 293 | log.warn( |
294 | 'Unexpected', | ||
295 | ServiceToMainIpcMessage.ApiExposedInMainWorld, | ||
296 | 'from webContents', | ||
297 | event.sender.id, | ||
298 | ); | ||
299 | throw new Error('Invalid IPC call'); | ||
300 | } | ||
301 | return serviceInject; | ||
302 | }); | ||
291 | 303 | ||
292 | browserView.webContents.on('ipc-message', (_event, channel, ...args) => { | 304 | browserView.webContents.on('ipc-message', (_event, channel, ...args) => { |
293 | try { | 305 | try { |
@@ -303,8 +315,8 @@ async function createWindow(): Promise<unknown> { | |||
303 | log.error('Unknown IPC message:', channel, args); | 315 | log.error('Unknown IPC message:', channel, args); |
304 | break; | 316 | break; |
305 | } | 317 | } |
306 | } catch (err) { | 318 | } catch (error) { |
307 | log.error('Error while processing IPC message:', channel, args, err); | 319 | log.error('Error while processing IPC message:', channel, args, error); |
308 | } | 320 | } |
309 | }); | 321 | }); |
310 | 322 | ||
@@ -316,9 +328,7 @@ async function createWindow(): Promise<unknown> { | |||
316 | 328 | ||
317 | browserView.webContents.session.webRequest.onBeforeSendHeaders( | 329 | browserView.webContents.session.webRequest.onBeforeSendHeaders( |
318 | ({ url, requestHeaders }, callback) => { | 330 | ({ url, requestHeaders }, callback) => { |
319 | const requestUserAgent = url.match( | 331 | const requestUserAgent = /^[^:]+:\/\/accounts\.google\.[^./]+\//.test(url) |
320 | /^[^:]+:\/\/accounts\.google\.[^./]+\//, | ||
321 | ) | ||
322 | ? chromelessUserAgent | 332 | ? chromelessUserAgent |
323 | : userAgent; | 333 | : userAgent; |
324 | callback({ | 334 | callback({ |
@@ -332,15 +342,15 @@ async function createWindow(): Promise<unknown> { | |||
332 | 342 | ||
333 | browserView.webContents | 343 | browserView.webContents |
334 | .loadURL('https://gitlab.com/say-hi-to-sophie/sophie') | 344 | .loadURL('https://gitlab.com/say-hi-to-sophie/sophie') |
335 | .catch((err) => { | 345 | .catch((error) => { |
336 | log.error('Failed to load browser', err); | 346 | log.error('Failed to load browser', error); |
337 | }); | 347 | }); |
338 | 348 | ||
339 | return mainWindow.loadURL(pageUrl); | 349 | return mainWindow.loadURL(pageUrl); |
340 | } | 350 | } |
341 | 351 | ||
342 | app.on('second-instance', () => { | 352 | app.on('second-instance', () => { |
343 | if (mainWindow !== null) { | 353 | if (mainWindow !== undefined) { |
344 | if (!mainWindow.isVisible()) { | 354 | if (!mainWindow.isVisible()) { |
345 | mainWindow.show(); | 355 | mainWindow.show(); |
346 | } | 356 | } |
@@ -363,14 +373,14 @@ app | |||
363 | if (isDevelopment) { | 373 | if (isDevelopment) { |
364 | try { | 374 | try { |
365 | await installDevToolsExtensions(); | 375 | await installDevToolsExtensions(); |
366 | } catch (err) { | 376 | } catch (error) { |
367 | log.error('Failed to install devtools extensions', err); | 377 | log.error('Failed to install devtools extensions', error); |
368 | } | 378 | } |
369 | } | 379 | } |
370 | 380 | ||
371 | return createWindow(); | 381 | return createWindow(); |
372 | }) | 382 | }) |
373 | .catch((err) => { | 383 | .catch((error) => { |
374 | log.error('Failed to create window', err); | 384 | log.error('Failed to create window', error); |
375 | process.exit(1); | 385 | process.exit(1); |
376 | }); | 386 | }); |
diff --git a/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts b/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts index e92f706..a11a9da 100644 --- a/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts +++ b/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts | |||
@@ -17,9 +17,9 @@ | |||
17 | * | 17 | * |
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | import { watch } from 'fs'; | 20 | import { watch } from 'node:fs'; |
21 | import { readFile, stat, writeFile } from 'fs/promises'; | 21 | import { readFile, stat, writeFile } from 'node:fs/promises'; |
22 | import { join } from 'path'; | 22 | import path from 'node:path'; |
23 | 23 | ||
24 | import JSON5 from 'json5'; | 24 | import JSON5 from 'json5'; |
25 | import throttle from 'lodash-es/throttle'; | 25 | import throttle from 'lodash-es/throttle'; |
@@ -39,26 +39,26 @@ export default class ConfigPersistenceServiceImpl | |||
39 | 39 | ||
40 | private writingConfig = false; | 40 | private writingConfig = false; |
41 | 41 | ||
42 | private timeLastWritten: Date | null = null; | 42 | private timeLastWritten: Date | undefined; |
43 | 43 | ||
44 | constructor( | 44 | constructor( |
45 | private readonly userDataDir: string, | 45 | private readonly userDataDir: string, |
46 | private readonly configFileName: string = 'config.json5', | 46 | private readonly configFileName: string = 'config.json5', |
47 | ) { | 47 | ) { |
48 | this.configFileName = configFileName; | 48 | this.configFileName = configFileName; |
49 | this.configFilePath = join(this.userDataDir, this.configFileName); | 49 | this.configFilePath = path.join(this.userDataDir, this.configFileName); |
50 | } | 50 | } |
51 | 51 | ||
52 | async readConfig(): Promise<ReadConfigResult> { | 52 | async readConfig(): Promise<ReadConfigResult> { |
53 | let configStr; | 53 | let configStr; |
54 | try { | 54 | try { |
55 | configStr = await readFile(this.configFilePath, 'utf8'); | 55 | configStr = await readFile(this.configFilePath, 'utf8'); |
56 | } catch (err) { | 56 | } catch (error) { |
57 | if ((err as NodeJS.ErrnoException).code === 'ENOENT') { | 57 | if ((error as NodeJS.ErrnoException).code === 'ENOENT') { |
58 | log.debug('Config file', this.configFilePath, 'was not found'); | 58 | log.debug('Config file', this.configFilePath, 'was not found'); |
59 | return { found: false }; | 59 | return { found: false }; |
60 | } | 60 | } |
61 | throw err; | 61 | throw error; |
62 | } | 62 | } |
63 | log.info('Read config file', this.configFilePath); | 63 | log.info('Read config file', this.configFilePath); |
64 | return { | 64 | return { |
@@ -92,8 +92,8 @@ export default class ConfigPersistenceServiceImpl | |||
92 | const stats = await stat(this.configFilePath); | 92 | const stats = await stat(this.configFilePath); |
93 | mtime = stats.mtime; | 93 | mtime = stats.mtime; |
94 | log.trace('Config file last modified at', mtime); | 94 | log.trace('Config file last modified at', mtime); |
95 | } catch (err) { | 95 | } catch (error) { |
96 | if ((err as NodeJS.ErrnoException).code === 'ENOENT') { | 96 | if ((error as NodeJS.ErrnoException).code === 'ENOENT') { |
97 | log.debug( | 97 | log.debug( |
98 | 'Config file', | 98 | 'Config file', |
99 | this.configFilePath, | 99 | this.configFilePath, |
@@ -101,11 +101,11 @@ export default class ConfigPersistenceServiceImpl | |||
101 | ); | 101 | ); |
102 | return; | 102 | return; |
103 | } | 103 | } |
104 | throw err; | 104 | throw error; |
105 | } | 105 | } |
106 | if ( | 106 | if ( |
107 | !this.writingConfig && | 107 | !this.writingConfig && |
108 | (this.timeLastWritten === null || mtime > this.timeLastWritten) | 108 | (this.timeLastWritten === undefined || mtime > this.timeLastWritten) |
109 | ) { | 109 | ) { |
110 | log.debug( | 110 | log.debug( |
111 | 'Found a config file modified at', | 111 | 'Found a config file modified at', |
diff --git a/packages/main/src/stores/Config.ts b/packages/main/src/stores/Config.ts index 06dbdeb..ca90c0c 100644 --- a/packages/main/src/stores/Config.ts +++ b/packages/main/src/stores/Config.ts | |||
@@ -18,12 +18,7 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { | 21 | import { config as originalConfig, ThemeSource } from '@sophie/shared'; |
22 | config as originalConfig, | ||
23 | ConfigSnapshotIn, | ||
24 | ConfigSnapshotOut, | ||
25 | ThemeSource, | ||
26 | } from '@sophie/shared'; | ||
27 | import { Instance } from 'mobx-state-tree'; | 22 | import { Instance } from 'mobx-state-tree'; |
28 | 23 | ||
29 | export const config = originalConfig.actions((self) => ({ | 24 | export const config = originalConfig.actions((self) => ({ |
@@ -34,4 +29,4 @@ export const config = originalConfig.actions((self) => ({ | |||
34 | 29 | ||
35 | export interface Config extends Instance<typeof config> {} | 30 | export interface Config extends Instance<typeof config> {} |
36 | 31 | ||
37 | export type { ConfigSnapshotIn, ConfigSnapshotOut }; | 32 | export type { ConfigSnapshotIn, ConfigSnapshotOut } from '@sophie/shared'; |
diff --git a/packages/main/src/utils/log.ts b/packages/main/src/utils/log.ts index 5218721..0a632d8 100644 --- a/packages/main/src/utils/log.ts +++ b/packages/main/src/utils/log.ts | |||
@@ -18,8 +18,9 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | // eslint-disable-next-line unicorn/import-style -- Import the type `ChalkInstance` separately. | ||
21 | import chalk, { ChalkInstance } from 'chalk'; | 22 | import chalk, { ChalkInstance } from 'chalk'; |
22 | import loglevel, { Logger } from 'loglevel'; | 23 | import loglevel from 'loglevel'; |
23 | import prefix from 'loglevel-plugin-prefix'; | 24 | import prefix from 'loglevel-plugin-prefix'; |
24 | 25 | ||
25 | if (import.meta.env?.DEV) { | 26 | if (import.meta.env?.DEV) { |
@@ -54,7 +55,7 @@ prefix.apply(loglevel, { | |||
54 | }, | 55 | }, |
55 | }); | 56 | }); |
56 | 57 | ||
57 | export function getLogger(loggerName: string): Logger { | 58 | export function getLogger(loggerName: string): loglevel.Logger { |
58 | return loglevel.getLogger(loggerName); | 59 | return loglevel.getLogger(loggerName); |
59 | } | 60 | } |
60 | 61 | ||
diff --git a/packages/preload/esbuild.config.js b/packages/preload/esbuild.config.js index d888987..87e91d8 100644 --- a/packages/preload/esbuild.config.js +++ b/packages/preload/esbuild.config.js | |||
@@ -1,9 +1,9 @@ | |||
1 | import { chrome } from '../../config/buildConstants.js'; | 1 | import { chrome } from '../../config/buildConstants.js'; |
2 | import fileURLToDirname from '../../config/fileURLToDirname.js'; | 2 | import fileUrlToDirname from '../../config/fileUrlToDirname.js'; |
3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; | 3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; |
4 | 4 | ||
5 | export default getEsbuildConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileUrlToDirname(import.meta.url), |
7 | entryPoints: ['src/index.ts'], | 7 | entryPoints: ['src/index.ts'], |
8 | outfile: 'dist/index.cjs', | 8 | outfile: 'dist/index.cjs', |
9 | format: 'cjs', | 9 | format: 'cjs', |
diff --git a/packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts b/packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts index b0af280..88b0077 100644 --- a/packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts +++ b/packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts | |||
@@ -45,7 +45,7 @@ const { default: createSophieRenderer } = await import( | |||
45 | ); | 45 | ); |
46 | 46 | ||
47 | const event: Electron.IpcRendererEvent = | 47 | const event: Electron.IpcRendererEvent = |
48 | null as unknown as Electron.IpcRendererEvent; | 48 | undefined as unknown as Electron.IpcRendererEvent; |
49 | 49 | ||
50 | const snapshot: SharedStoreSnapshotIn = { | 50 | const snapshot: SharedStoreSnapshotIn = { |
51 | shouldUseDarkColors: true, | 51 | shouldUseDarkColors: true, |
@@ -183,7 +183,7 @@ describe('SharedStoreConnector', () => { | |||
183 | 183 | ||
184 | it('should catch listener errors', () => { | 184 | it('should catch listener errors', () => { |
185 | mocked(listener.onPatch).mockImplementation(() => { | 185 | mocked(listener.onPatch).mockImplementation(() => { |
186 | throw new Error(); | 186 | throw new Error('listener error'); |
187 | }); | 187 | }); |
188 | expect(() => onSharedStorePatch(event, patch)).not.toThrow(); | 188 | expect(() => onSharedStorePatch(event, patch)).not.toThrow(); |
189 | }); | 189 | }); |
@@ -193,7 +193,7 @@ describe('SharedStoreConnector', () => { | |||
193 | describe('after the listener threw in onPatch', () => { | 193 | describe('after the listener threw in onPatch', () => { |
194 | beforeEach(() => { | 194 | beforeEach(() => { |
195 | mocked(listener.onPatch).mockImplementation(() => { | 195 | mocked(listener.onPatch).mockImplementation(() => { |
196 | throw new Error(); | 196 | throw new Error('listener error'); |
197 | }); | 197 | }); |
198 | onSharedStorePatch(event, patch); | 198 | onSharedStorePatch(event, patch); |
199 | listener.onPatch.mockRestore(); | 199 | listener.onPatch.mockRestore(); |
@@ -205,7 +205,9 @@ describe('SharedStoreConnector', () => { | |||
205 | 205 | ||
206 | describe('when a listener failed to register due to IPC error', () => { | 206 | describe('when a listener failed to register due to IPC error', () => { |
207 | beforeEach(async () => { | 207 | beforeEach(async () => { |
208 | mocked(ipcRenderer.invoke).mockRejectedValue(new Error()); | 208 | mocked(ipcRenderer.invoke).mockRejectedValue( |
209 | new Error('ipcRenderer error'), | ||
210 | ); | ||
209 | try { | 211 | try { |
210 | await sut.onSharedStoreChange(listener); | 212 | await sut.onSharedStoreChange(listener); |
211 | } catch { | 213 | } catch { |
@@ -237,7 +239,7 @@ describe('SharedStoreConnector', () => { | |||
237 | beforeEach(async () => { | 239 | beforeEach(async () => { |
238 | mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot); | 240 | mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot); |
239 | mocked(listener.onSnapshot).mockImplementation(() => { | 241 | mocked(listener.onSnapshot).mockImplementation(() => { |
240 | throw new Error(); | 242 | throw new Error('listener error'); |
241 | }); | 243 | }); |
242 | try { | 244 | try { |
243 | await sut.onSharedStoreChange(listener); | 245 | await sut.onSharedStoreChange(listener); |
diff --git a/packages/preload/src/contextBridge/createSophieRenderer.ts b/packages/preload/src/contextBridge/createSophieRenderer.ts index b97503d..3174fed 100644 --- a/packages/preload/src/contextBridge/createSophieRenderer.ts +++ b/packages/preload/src/contextBridge/createSophieRenderer.ts | |||
@@ -34,7 +34,7 @@ import type { IJsonPatch } from 'mobx-state-tree'; | |||
34 | class SharedStoreConnector { | 34 | class SharedStoreConnector { |
35 | private onSharedStoreChangeCalled = false; | 35 | private onSharedStoreChangeCalled = false; |
36 | 36 | ||
37 | private listener: SharedStoreListener | null = null; | 37 | private listener: SharedStoreListener | undefined; |
38 | 38 | ||
39 | constructor(private readonly allowReplaceListener: boolean) { | 39 | constructor(private readonly allowReplaceListener: boolean) { |
40 | ipcRenderer.on( | 40 | ipcRenderer.on( |
@@ -43,9 +43,9 @@ class SharedStoreConnector { | |||
43 | try { | 43 | try { |
44 | // `mobx-state-tree` will validate the patch, so we can safely cast here. | 44 | // `mobx-state-tree` will validate the patch, so we can safely cast here. |
45 | this.listener?.onPatch(patch as IJsonPatch); | 45 | this.listener?.onPatch(patch as IJsonPatch); |
46 | } catch (err) { | 46 | } catch (error) { |
47 | log.error('Shared store listener onPatch failed', err); | 47 | log.error('Shared store listener onPatch failed', error); |
48 | this.listener = null; | 48 | this.listener = undefined; |
49 | } | 49 | } |
50 | }, | 50 | }, |
51 | ); | 51 | ); |
@@ -57,14 +57,14 @@ class SharedStoreConnector { | |||
57 | } | 57 | } |
58 | this.onSharedStoreChangeCalled = true; | 58 | this.onSharedStoreChangeCalled = true; |
59 | let success = false; | 59 | let success = false; |
60 | let snapshot: unknown | null = null; | 60 | let snapshot: unknown; |
61 | try { | 61 | try { |
62 | snapshot = await ipcRenderer.invoke( | 62 | snapshot = await ipcRenderer.invoke( |
63 | RendererToMainIpcMessage.GetSharedStoreSnapshot, | 63 | RendererToMainIpcMessage.GetSharedStoreSnapshot, |
64 | ); | 64 | ); |
65 | success = true; | 65 | success = true; |
66 | } catch (err) { | 66 | } catch (error) { |
67 | log.error('Failed to get initial shared store snapshot', err); | 67 | log.error('Failed to get initial shared store snapshot', error); |
68 | } | 68 | } |
69 | if (success) { | 69 | if (success) { |
70 | if (sharedStore.is(snapshot)) { | 70 | if (sharedStore.is(snapshot)) { |
@@ -84,10 +84,10 @@ function dispatchAction(actionToDispatch: Action): void { | |||
84 | const parsedAction = action.parse(actionToDispatch); | 84 | const parsedAction = action.parse(actionToDispatch); |
85 | try { | 85 | try { |
86 | ipcRenderer.send(RendererToMainIpcMessage.DispatchAction, parsedAction); | 86 | ipcRenderer.send(RendererToMainIpcMessage.DispatchAction, parsedAction); |
87 | } catch (err) { | 87 | } catch (error) { |
88 | // Do not leak IPC failure details into the main world. | 88 | // Do not leak IPC failure details into the main world. |
89 | const message = 'Failed to dispatch action'; | 89 | const message = 'Failed to dispatch action'; |
90 | log.error(message, actionToDispatch, err); | 90 | log.error(message, actionToDispatch, error); |
91 | throw new Error(message); | 91 | throw new Error(message); |
92 | } | 92 | } |
93 | } | 93 | } |
diff --git a/packages/renderer/src/components/BrowserViewPlaceholder.tsx b/packages/renderer/src/components/BrowserViewPlaceholder.tsx index 8f055e7..58407ee 100644 --- a/packages/renderer/src/components/BrowserViewPlaceholder.tsx +++ b/packages/renderer/src/components/BrowserViewPlaceholder.tsx | |||
@@ -44,15 +44,15 @@ export default observer(() => { | |||
44 | [store], | 44 | [store], |
45 | ); | 45 | ); |
46 | 46 | ||
47 | const resizeObserverRef = useRef<ResizeObserver | null>(null); | 47 | const resizeObserverRef = useRef<ResizeObserver | undefined>(); |
48 | 48 | ||
49 | const ref = useCallback( | 49 | const ref = useCallback( |
50 | (element: HTMLElement | null) => { | 50 | (element: HTMLElement | null) => { |
51 | if (resizeObserverRef.current !== null) { | 51 | if (resizeObserverRef.current !== undefined) { |
52 | resizeObserverRef.current.disconnect(); | 52 | resizeObserverRef.current.disconnect(); |
53 | } | 53 | } |
54 | if (element === null) { | 54 | if (element === null) { |
55 | resizeObserverRef.current = null; | 55 | resizeObserverRef.current = undefined; |
56 | return; | 56 | return; |
57 | } | 57 | } |
58 | resizeObserverRef.current = new ResizeObserver(onResize); | 58 | resizeObserverRef.current = new ResizeObserver(onResize); |
diff --git a/packages/renderer/src/components/StoreProvider.tsx b/packages/renderer/src/components/StoreProvider.tsx index bb8495c..3360a43 100644 --- a/packages/renderer/src/components/StoreProvider.tsx +++ b/packages/renderer/src/components/StoreProvider.tsx | |||
@@ -22,11 +22,12 @@ import React, { createContext, useContext } from 'react'; | |||
22 | 22 | ||
23 | import type { RendererStore } from '../stores/RendererStore'; | 23 | import type { RendererStore } from '../stores/RendererStore'; |
24 | 24 | ||
25 | const StoreContext = createContext<RendererStore | null>(null); | 25 | // eslint-disable-next-line unicorn/no-useless-undefined -- `createContext` expects 1 parameter. |
26 | const StoreContext = createContext<RendererStore | undefined>(undefined); | ||
26 | 27 | ||
27 | export function useStore(): RendererStore { | 28 | export function useStore(): RendererStore { |
28 | const store = useContext(StoreContext); | 29 | const store = useContext(StoreContext); |
29 | if (store === null) { | 30 | if (store === undefined) { |
30 | throw new Error('useStore can only be called inside of StoreProvider'); | 31 | throw new Error('useStore can only be called inside of StoreProvider'); |
31 | } | 32 | } |
32 | return store; | 33 | return store; |
diff --git a/packages/renderer/src/index.tsx b/packages/renderer/src/index.tsx index d900e50..a42a30c 100644 --- a/packages/renderer/src/index.tsx +++ b/packages/renderer/src/index.tsx | |||
@@ -45,8 +45,8 @@ if (isDevelopment) { | |||
45 | const store = createAndConnectRendererStore(window.sophieRenderer); | 45 | const store = createAndConnectRendererStore(window.sophieRenderer); |
46 | 46 | ||
47 | if (isDevelopment) { | 47 | if (isDevelopment) { |
48 | exposeToReduxDevtools(store).catch((err) => { | 48 | exposeToReduxDevtools(store).catch((error) => { |
49 | log.error('Cannot initialize redux devtools', err); | 49 | log.error('Cannot initialize redux devtools', error); |
50 | }); | 50 | }); |
51 | } | 51 | } |
52 | 52 | ||
diff --git a/packages/renderer/src/stores/RendererStore.ts b/packages/renderer/src/stores/RendererStore.ts index 0b78ce1..4cc5163 100644 --- a/packages/renderer/src/stores/RendererStore.ts +++ b/packages/renderer/src/stores/RendererStore.ts | |||
@@ -86,8 +86,8 @@ export function createAndConnectRendererStore( | |||
86 | applyPatch(store.shared, patch); | 86 | applyPatch(store.shared, patch); |
87 | }, | 87 | }, |
88 | }) | 88 | }) |
89 | .catch((err) => { | 89 | .catch((error) => { |
90 | log.error('Failed to connect to shared store', err); | 90 | log.error('Failed to connect to shared store', error); |
91 | }); | 91 | }); |
92 | 92 | ||
93 | return store; | 93 | return store; |
diff --git a/packages/renderer/vite.config.js b/packages/renderer/vite.config.js index e20e0f1..cb0203c 100644 --- a/packages/renderer/vite.config.js +++ b/packages/renderer/vite.config.js | |||
@@ -1,15 +1,15 @@ | |||
1 | /* eslint-disable no-process-env */ | 1 | /* eslint-disable no-process-env */ |
2 | /* eslint-env node */ | 2 | /* eslint-env node */ |
3 | 3 | ||
4 | import { builtinModules } from 'module'; | 4 | import { builtinModules } from 'node:module'; |
5 | import { join } from 'path'; | 5 | import path from 'node:path'; |
6 | 6 | ||
7 | import react from '@vitejs/plugin-react'; | 7 | import react from '@vitejs/plugin-react'; |
8 | 8 | ||
9 | import { banner, chrome } from '../../config/buildConstants.js'; | 9 | import { banner, chrome } from '../../config/buildConstants.js'; |
10 | import fileURLToDirname from '../../config/fileURLToDirname.js'; | 10 | import fileUrlToDirname from '../../config/fileUrlToDirname.js'; |
11 | 11 | ||
12 | const thisDir = fileURLToDirname(import.meta.url); | 12 | const thisDir = fileUrlToDirname(import.meta.url); |
13 | 13 | ||
14 | const mode = process.env.MODE || 'development'; | 14 | const mode = process.env.MODE || 'development'; |
15 | 15 | ||
@@ -24,7 +24,7 @@ export default { | |||
24 | logLevel: 'info', | 24 | logLevel: 'info', |
25 | mode, | 25 | mode, |
26 | root: thisDir, | 26 | root: thisDir, |
27 | cacheDir: join(thisDir, '../../.vite'), | 27 | cacheDir: path.join(thisDir, '../../.vite'), |
28 | plugins: [ | 28 | plugins: [ |
29 | react({ | 29 | react({ |
30 | babel: { | 30 | babel: { |
diff --git a/packages/service-inject/esbuild.config.js b/packages/service-inject/esbuild.config.js index 795b0f6..d8698ac 100644 --- a/packages/service-inject/esbuild.config.js +++ b/packages/service-inject/esbuild.config.js | |||
@@ -1,9 +1,9 @@ | |||
1 | import { chrome } from '../../config/buildConstants.js'; | 1 | import { chrome } from '../../config/buildConstants.js'; |
2 | import fileURLToDirname from '../../config/fileURLToDirname.js'; | 2 | import fileUrlToDirname from '../../config/fileUrlToDirname.js'; |
3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; | 3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; |
4 | 4 | ||
5 | export default getEsbuildConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileUrlToDirname(import.meta.url), |
7 | entryPoints: ['src/index.ts'], | 7 | entryPoints: ['src/index.ts'], |
8 | outfile: 'dist/index.js', | 8 | outfile: 'dist/index.js', |
9 | format: 'iife', | 9 | format: 'iife', |
diff --git a/packages/service-preload/esbuild.config.js b/packages/service-preload/esbuild.config.js index d888987..87e91d8 100644 --- a/packages/service-preload/esbuild.config.js +++ b/packages/service-preload/esbuild.config.js | |||
@@ -1,9 +1,9 @@ | |||
1 | import { chrome } from '../../config/buildConstants.js'; | 1 | import { chrome } from '../../config/buildConstants.js'; |
2 | import fileURLToDirname from '../../config/fileURLToDirname.js'; | 2 | import fileUrlToDirname from '../../config/fileUrlToDirname.js'; |
3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; | 3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; |
4 | 4 | ||
5 | export default getEsbuildConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileUrlToDirname(import.meta.url), |
7 | entryPoints: ['src/index.ts'], | 7 | entryPoints: ['src/index.ts'], |
8 | outfile: 'dist/index.cjs', | 8 | outfile: 'dist/index.cjs', |
9 | format: 'cjs', | 9 | format: 'cjs', |
diff --git a/packages/service-preload/src/index.ts b/packages/service-preload/src/index.ts index 2bbfefd..bb4a62d 100644 --- a/packages/service-preload/src/index.ts +++ b/packages/service-preload/src/index.ts | |||
@@ -61,6 +61,6 @@ async function fetchAndExecuteInjectScript(): Promise<void> { | |||
61 | await webFrame.executeJavaScriptInIsolatedWorld(0, [injectSource]); | 61 | await webFrame.executeJavaScriptInIsolatedWorld(0, [injectSource]); |
62 | } | 62 | } |
63 | 63 | ||
64 | fetchAndExecuteInjectScript().catch((err) => { | 64 | fetchAndExecuteInjectScript().catch((error) => { |
65 | log.error('Failed to fetch inject source:', err); | 65 | log.error('Failed to fetch inject source:', error); |
66 | }); | 66 | }); |
diff --git a/packages/service-shared/esbuild.config.js b/packages/service-shared/esbuild.config.js index 2b0dec8..62e3d2a 100644 --- a/packages/service-shared/esbuild.config.js +++ b/packages/service-shared/esbuild.config.js | |||
@@ -1,9 +1,9 @@ | |||
1 | import { chrome, node } from '../../config/buildConstants.js'; | 1 | import { chrome, node } from '../../config/buildConstants.js'; |
2 | import fileURLToDirname from '../../config/fileURLToDirname.js'; | 2 | import fileUrlToDirname from '../../config/fileUrlToDirname.js'; |
3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; | 3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; |
4 | 4 | ||
5 | export default getEsbuildConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileUrlToDirname(import.meta.url), |
7 | entryPoints: ['src/index.ts'], | 7 | entryPoints: ['src/index.ts'], |
8 | outfile: 'dist/index.mjs', | 8 | outfile: 'dist/index.mjs', |
9 | format: 'esm', | 9 | format: 'esm', |
diff --git a/packages/shared/esbuild.config.js b/packages/shared/esbuild.config.js index 44501bd..7a79ce6 100644 --- a/packages/shared/esbuild.config.js +++ b/packages/shared/esbuild.config.js | |||
@@ -1,9 +1,9 @@ | |||
1 | import { chrome, node } from '../../config/buildConstants.js'; | 1 | import { chrome, node } from '../../config/buildConstants.js'; |
2 | import fileURLToDirname from '../../config/fileURLToDirname.js'; | 2 | import fileUrlToDirname from '../../config/fileUrlToDirname.js'; |
3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; | 3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; |
4 | 4 | ||
5 | export default getEsbuildConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileUrlToDirname(import.meta.url), |
7 | entryPoints: ['src/index.ts'], | 7 | entryPoints: ['src/index.ts'], |
8 | outfile: 'dist/index.mjs', | 8 | outfile: 'dist/index.mjs', |
9 | format: 'esm', | 9 | format: 'esm', |