diff options
Diffstat (limited to 'packages')
68 files changed, 485 insertions, 282 deletions
diff --git a/packages/main/.eslintrc.cjs b/packages/main/.eslintrc.cjs new file mode 100644 index 0000000..548ea34 --- /dev/null +++ b/packages/main/.eslintrc.cjs | |||
@@ -0,0 +1,6 @@ | |||
1 | module.exports = { | ||
2 | env: { | ||
3 | node: true, | ||
4 | browser: false, | ||
5 | }, | ||
6 | }; | ||
diff --git a/packages/main/.eslintrc.json b/packages/main/.eslintrc.json deleted file mode 100644 index 6b736e2..0000000 --- a/packages/main/.eslintrc.json +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | { | ||
2 | "globals": { | ||
3 | "NodeJS": false, | ||
4 | "require": false | ||
5 | } | ||
6 | } | ||
diff --git a/packages/main/esbuild.config.js b/packages/main/esbuild.config.js index c24d6e1..49fba6b 100644 --- a/packages/main/esbuild.config.js +++ b/packages/main/esbuild.config.js | |||
@@ -1,8 +1,8 @@ | |||
1 | /* eslint-disable no-process-env */ | ||
2 | import getRepoInfo from 'git-repo-info'; | 1 | import getRepoInfo from 'git-repo-info'; |
2 | |||
3 | import { node } from '../../config/buildConstants.js'; | 3 | import { node } from '../../config/buildConstants.js'; |
4 | import { getConfig } from '../../config/esbuildConfig.js'; | 4 | import fileURLToDirname from '../../config/fileURLToDirname.js'; |
5 | import { fileURLToDirname } from '../../config/utils.js'; | 5 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; |
6 | 6 | ||
7 | const externalPackages = ['electron']; | 7 | const externalPackages = ['electron']; |
8 | 8 | ||
@@ -12,7 +12,7 @@ if (process.env.MODE !== 'development') { | |||
12 | 12 | ||
13 | const gitInfo = getRepoInfo(); | 13 | const gitInfo = getRepoInfo(); |
14 | 14 | ||
15 | export default getConfig({ | 15 | export default getEsbuildConfig({ |
16 | absWorkingDir: fileURLToDirname(import.meta.url), | 16 | absWorkingDir: fileURLToDirname(import.meta.url), |
17 | entryPoints: [ | 17 | entryPoints: [ |
18 | 'src/index.ts', | 18 | 'src/index.ts', |
diff --git a/packages/main/package.json b/packages/main/package.json index e1b3f49..d9abf51 100644 --- a/packages/main/package.json +++ b/packages/main/package.json | |||
@@ -5,7 +5,7 @@ | |||
5 | "type": "module", | 5 | "type": "module", |
6 | "types": "dist-types/index.d.ts", | 6 | "types": "dist-types/index.d.ts", |
7 | "scripts": { | 7 | "scripts": { |
8 | "typecheck": "tsc" | 8 | "typecheck:workspace": "yarn g:typecheck" |
9 | }, | 9 | }, |
10 | "dependencies": { | 10 | "dependencies": { |
11 | "@sophie/service-shared": "workspace:*", | 11 | "@sophie/service-shared": "workspace:*", |
diff --git a/packages/main/src/controllers/__tests__/config.spec.ts b/packages/main/src/controllers/__tests__/initConfig.spec.ts index eb67df0..e386a07 100644 --- a/packages/main/src/controllers/__tests__/config.spec.ts +++ b/packages/main/src/controllers/__tests__/initConfig.spec.ts | |||
@@ -22,20 +22,20 @@ import { jest } from '@jest/globals'; | |||
22 | import { mocked } from 'jest-mock'; | 22 | import { mocked } from 'jest-mock'; |
23 | import ms from 'ms'; | 23 | import ms from 'ms'; |
24 | 24 | ||
25 | import { initConfig } from '../config'; | 25 | import type ConfigPersistenceService from '../../services/ConfigPersistenceService'; |
26 | import type { ConfigPersistenceService } from '../../services/ConfigPersistenceService'; | ||
27 | import { Config, config as configModel } from '../../stores/Config'; | 26 | import { Config, config as configModel } from '../../stores/Config'; |
28 | import { Disposer } from '../../utils/disposer'; | 27 | import type Disposer from '../../utils/Disposer'; |
29 | import { silenceLogger } from '../../utils/logging'; | 28 | import { silenceLogger } from '../../utils/log'; |
29 | import initConfig from '../initConfig'; | ||
30 | 30 | ||
31 | let config: Config; | 31 | let config: Config; |
32 | let persistenceService: ConfigPersistenceService = { | 32 | const persistenceService: ConfigPersistenceService = { |
33 | readConfig: jest.fn(), | 33 | readConfig: jest.fn(), |
34 | writeConfig: jest.fn(), | 34 | writeConfig: jest.fn(), |
35 | watchConfig: jest.fn(), | 35 | watchConfig: jest.fn(), |
36 | }; | 36 | }; |
37 | let lessThanThrottleMs = ms('0.1s'); | 37 | const lessThanThrottleMs = ms('0.1s'); |
38 | let throttleMs = ms('1s'); | 38 | const throttleMs = ms('1s'); |
39 | 39 | ||
40 | beforeAll(() => { | 40 | beforeAll(() => { |
41 | jest.useFakeTimers(); | 41 | jest.useFakeTimers(); |
@@ -108,7 +108,7 @@ describe('when initializing', () => { | |||
108 | 108 | ||
109 | describe('when it has loaded the config', () => { | 109 | describe('when it has loaded the config', () => { |
110 | let sutDisposer: Disposer; | 110 | let sutDisposer: Disposer; |
111 | let watcherDisposer: Disposer = jest.fn(); | 111 | const watcherDisposer: Disposer = jest.fn(); |
112 | let configChangedCallback: () => Promise<void>; | 112 | let configChangedCallback: () => Promise<void>; |
113 | 113 | ||
114 | beforeEach(async () => { | 114 | beforeEach(async () => { |
@@ -118,7 +118,7 @@ describe('when it has loaded the config', () => { | |||
118 | }); | 118 | }); |
119 | mocked(persistenceService.watchConfig).mockReturnValueOnce(watcherDisposer); | 119 | mocked(persistenceService.watchConfig).mockReturnValueOnce(watcherDisposer); |
120 | sutDisposer = await initConfig(config, persistenceService, throttleMs); | 120 | sutDisposer = await initConfig(config, persistenceService, throttleMs); |
121 | configChangedCallback = mocked(persistenceService.watchConfig).mock.calls[0][0]; | 121 | [[configChangedCallback]] = mocked(persistenceService.watchConfig).mock.calls; |
122 | jest.resetAllMocks(); | 122 | jest.resetAllMocks(); |
123 | }); | 123 | }); |
124 | 124 | ||
diff --git a/packages/main/src/controllers/__tests__/nativeTheme.spec.ts b/packages/main/src/controllers/__tests__/initNativeTheme.spec.ts index 85d6dd2..bd33f48 100644 --- a/packages/main/src/controllers/__tests__/nativeTheme.spec.ts +++ b/packages/main/src/controllers/__tests__/initNativeTheme.spec.ts | |||
@@ -22,7 +22,7 @@ import { jest } from '@jest/globals'; | |||
22 | import { mocked } from 'jest-mock'; | 22 | import { mocked } from 'jest-mock'; |
23 | 23 | ||
24 | import { createMainStore, MainStore } from '../../stores/MainStore'; | 24 | import { createMainStore, MainStore } from '../../stores/MainStore'; |
25 | import { Disposer } from '../../utils/disposer'; | 25 | import type Disposer from '../../utils/Disposer'; |
26 | 26 | ||
27 | let shouldUseDarkColors = false; | 27 | let shouldUseDarkColors = false; |
28 | 28 | ||
@@ -38,7 +38,7 @@ jest.unstable_mockModule('electron', () => ({ | |||
38 | })); | 38 | })); |
39 | 39 | ||
40 | const { nativeTheme } = await import('electron'); | 40 | const { nativeTheme } = await import('electron'); |
41 | const { initNativeTheme } = await import('../nativeTheme'); | 41 | const { default: initNativeTheme } = await import('../initNativeTheme'); |
42 | 42 | ||
43 | let store: MainStore; | 43 | let store: MainStore; |
44 | let disposeSut: Disposer; | 44 | let disposeSut: Disposer; |
diff --git a/packages/main/src/controllers/config.ts b/packages/main/src/controllers/initConfig.ts index deaeac2..1d40762 100644 --- a/packages/main/src/controllers/config.ts +++ b/packages/main/src/controllers/initConfig.ts | |||
@@ -19,19 +19,19 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { debounce } from 'lodash-es'; | 21 | import { debounce } from 'lodash-es'; |
22 | import ms from 'ms'; | ||
23 | import { applySnapshot, getSnapshot, onSnapshot } from 'mobx-state-tree'; | 22 | import { applySnapshot, getSnapshot, onSnapshot } from 'mobx-state-tree'; |
23 | import ms from 'ms'; | ||
24 | 24 | ||
25 | import type { ConfigPersistenceService } from '../services/ConfigPersistenceService.js'; | 25 | import type ConfigPersistenceService from '../services/ConfigPersistenceService'; |
26 | import type { Config, ConfigSnapshotOut } from '../stores/Config.js'; | 26 | import type { Config, ConfigSnapshotOut } from '../stores/Config'; |
27 | import { Disposer } from '../utils/disposer'; | 27 | import type Disposer from '../utils/Disposer'; |
28 | import { getLogger } from '../utils/logging'; | 28 | import { getLogger } from '../utils/log'; |
29 | 29 | ||
30 | const DEFAULT_CONFIG_DEBOUNCE_TIME = ms('1s'); | 30 | const DEFAULT_CONFIG_DEBOUNCE_TIME = ms('1s'); |
31 | 31 | ||
32 | const log = getLogger('config'); | 32 | const log = getLogger('config'); |
33 | 33 | ||
34 | export async function initConfig( | 34 | export default async function initConfig( |
35 | config: Config, | 35 | config: Config, |
36 | persistenceService: ConfigPersistenceService, | 36 | persistenceService: ConfigPersistenceService, |
37 | debounceTime: number = DEFAULT_CONFIG_DEBOUNCE_TIME, | 37 | debounceTime: number = DEFAULT_CONFIG_DEBOUNCE_TIME, |
diff --git a/packages/main/src/controllers/nativeTheme.ts b/packages/main/src/controllers/initNativeTheme.ts index ccd12d8..d2074ab 100644 --- a/packages/main/src/controllers/nativeTheme.ts +++ b/packages/main/src/controllers/initNativeTheme.ts | |||
@@ -21,13 +21,13 @@ | |||
21 | import { nativeTheme } from 'electron'; | 21 | import { nativeTheme } from 'electron'; |
22 | import { autorun } from 'mobx'; | 22 | import { autorun } from 'mobx'; |
23 | 23 | ||
24 | import type { MainStore } from '../stores/MainStore.js'; | 24 | import type { MainStore } from '../stores/MainStore'; |
25 | import { Disposer } from '../utils/disposer'; | 25 | import type Disposer from '../utils/Disposer'; |
26 | import { getLogger } from '../utils/logging'; | 26 | import { getLogger } from '../utils/log'; |
27 | 27 | ||
28 | const log = getLogger('nativeTheme'); | 28 | const log = getLogger('nativeTheme'); |
29 | 29 | ||
30 | export function initNativeTheme(store: MainStore): Disposer { | 30 | export default function initNativeTheme(store: MainStore): Disposer { |
31 | log.trace('Initializing nativeTheme controller'); | 31 | log.trace('Initializing nativeTheme controller'); |
32 | 32 | ||
33 | const disposeThemeSourceReaction = autorun(() => { | 33 | const disposeThemeSourceReaction = autorun(() => { |
diff --git a/packages/main/src/devTools.ts b/packages/main/src/devTools.ts index 398904c..0486c36 100644 --- a/packages/main/src/devTools.ts +++ b/packages/main/src/devTools.ts | |||
@@ -46,7 +46,12 @@ export async function installDevToolsExtensions(): Promise<void> { | |||
46 | default: installExtension, | 46 | default: installExtension, |
47 | REACT_DEVELOPER_TOOLS, | 47 | REACT_DEVELOPER_TOOLS, |
48 | REDUX_DEVTOOLS, | 48 | REDUX_DEVTOOLS, |
49 | } = require('electron-devtools-installer'); | 49 | /* eslint-disable-next-line |
50 | import/no-extraneous-dependencies, | ||
51 | global-require, | ||
52 | @typescript-eslint/no-var-requires | ||
53 | */ | ||
54 | } = require('electron-devtools-installer') as typeof import('electron-devtools-installer'); | ||
50 | await installExtension( | 55 | await installExtension( |
51 | [ | 56 | [ |
52 | REACT_DEVELOPER_TOOLS, | 57 | REACT_DEVELOPER_TOOLS, |
diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts index d0191b7..bc10b4c 100644 --- a/packages/main/src/index.ts +++ b/packages/main/src/index.ts | |||
@@ -19,18 +19,10 @@ | |||
19 | * SPDX-License-Identifier: AGPL-3.0-only | 19 | * SPDX-License-Identifier: AGPL-3.0-only |
20 | */ | 20 | */ |
21 | 21 | ||
22 | import { | ||
23 | app, | ||
24 | BrowserView, | ||
25 | BrowserWindow, | ||
26 | ipcMain, | ||
27 | } from 'electron'; | ||
28 | import { arch } from 'os'; | 22 | import { arch } from 'os'; |
29 | import osName from 'os-name'; | ||
30 | import { ensureDirSync, readFile, readFileSync } from 'fs-extra'; | ||
31 | import { autorun } from 'mobx'; | ||
32 | import { getSnapshot, onPatch } from 'mobx-state-tree'; | ||
33 | import { join } from 'path'; | 23 | import { join } from 'path'; |
24 | import { URL } from 'url'; | ||
25 | |||
34 | import { | 26 | import { |
35 | ServiceToMainIpcMessage, | 27 | ServiceToMainIpcMessage, |
36 | unreadCount, | 28 | unreadCount, |
@@ -41,18 +33,30 @@ import { | |||
41 | MainToRendererIpcMessage, | 33 | MainToRendererIpcMessage, |
42 | RendererToMainIpcMessage, | 34 | RendererToMainIpcMessage, |
43 | } from '@sophie/shared'; | 35 | } from '@sophie/shared'; |
44 | import { URL } from 'url'; | 36 | import { |
37 | app, | ||
38 | BrowserView, | ||
39 | BrowserWindow, | ||
40 | ipcMain, | ||
41 | } from 'electron'; | ||
42 | import { ensureDirSync, readFile, readFileSync } from 'fs-extra'; | ||
43 | import { autorun } from 'mobx'; | ||
44 | import { getSnapshot, onPatch } from 'mobx-state-tree'; | ||
45 | import osName from 'os-name'; | ||
45 | 46 | ||
46 | import { init } from './compositionRoot'; | ||
47 | import { | 47 | import { |
48 | DEVMODE_ALLOWED_URL_PREFIXES, | 48 | DEVMODE_ALLOWED_URL_PREFIXES, |
49 | installDevToolsExtensions, | 49 | installDevToolsExtensions, |
50 | openDevToolsWhenReady, | 50 | openDevToolsWhenReady, |
51 | } from './devTools'; | 51 | } from './devTools'; |
52 | import init from './init'; | ||
52 | import { createMainStore } from './stores/MainStore'; | 53 | import { createMainStore } from './stores/MainStore'; |
54 | import { getLogger } from './utils/log'; | ||
53 | 55 | ||
54 | const isDevelopment = import.meta.env.MODE === 'development'; | 56 | const isDevelopment = import.meta.env.MODE === 'development'; |
55 | 57 | ||
58 | const log = getLogger('index'); | ||
59 | |||
56 | // Always enable sandboxing. | 60 | // Always enable sandboxing. |
57 | app.enableSandbox(); | 61 | app.enableSandbox(); |
58 | 62 | ||
@@ -93,7 +97,7 @@ app.setAboutPanelOptions({ | |||
93 | `Node.js: ${process.versions.node}`, | 97 | `Node.js: ${process.versions.node}`, |
94 | `Platform: ${osName()}`, | 98 | `Platform: ${osName()}`, |
95 | `Arch: ${arch()}`, | 99 | `Arch: ${arch()}`, |
96 | `Build date: ${new Date(Number(import.meta.env.BUILD_DATE))}`, | 100 | `Build date: ${new Date(Number(import.meta.env.BUILD_DATE)).toLocaleString()}`, |
97 | `Git SHA: ${import.meta.env.GIT_SHA}`, | 101 | `Git SHA: ${import.meta.env.GIT_SHA}`, |
98 | `Git branch: ${import.meta.env.GIT_BRANCH}`, | 102 | `Git branch: ${import.meta.env.GIT_BRANCH}`, |
99 | ].join('\n'), | 103 | ].join('\n'), |
@@ -109,9 +113,9 @@ function getResourceUrl(relativePath: string): string { | |||
109 | return new URL(relativePath, baseUrl).toString(); | 113 | return new URL(relativePath, baseUrl).toString(); |
110 | } | 114 | } |
111 | 115 | ||
112 | let serviceInjectRelativePath = '../../service-inject/dist/index.js'; | 116 | const serviceInjectRelativePath = '../../service-inject/dist/index.js'; |
113 | let serviceInjectPath = getResourcePath(serviceInjectRelativePath); | 117 | const serviceInjectPath = getResourcePath(serviceInjectRelativePath); |
114 | let serviceInject: WebSource = { | 118 | const serviceInject: WebSource = { |
115 | code: readFileSync(serviceInjectPath, 'utf8'), | 119 | code: readFileSync(serviceInjectPath, 'utf8'), |
116 | url: getResourceUrl(serviceInjectRelativePath), | 120 | url: getResourceUrl(serviceInjectRelativePath), |
117 | }; | 121 | }; |
@@ -122,7 +126,7 @@ const store = createMainStore(); | |||
122 | init(store).then((disposeCompositionRoot) => { | 126 | init(store).then((disposeCompositionRoot) => { |
123 | app.on('will-quit', disposeCompositionRoot); | 127 | app.on('will-quit', disposeCompositionRoot); |
124 | }).catch((err) => { | 128 | }).catch((err) => { |
125 | console.log('Failed to initialize application', err); | 129 | log.log('Failed to initialize application', err); |
126 | }); | 130 | }); |
127 | 131 | ||
128 | const rendererBaseUrl = getResourceUrl('../renderer/'); | 132 | const rendererBaseUrl = getResourceUrl('../renderer/'); |
@@ -211,7 +215,7 @@ async function createWindow(): Promise<unknown> { | |||
211 | 215 | ||
212 | ipcMain.handle(RendererToMainIpcMessage.GetSharedStoreSnapshot, (event) => { | 216 | ipcMain.handle(RendererToMainIpcMessage.GetSharedStoreSnapshot, (event) => { |
213 | if (event.sender.id !== webContents.id) { | 217 | if (event.sender.id !== webContents.id) { |
214 | console.warn( | 218 | log.warn( |
215 | 'Unexpected', | 219 | 'Unexpected', |
216 | RendererToMainIpcMessage.GetSharedStoreSnapshot, | 220 | RendererToMainIpcMessage.GetSharedStoreSnapshot, |
217 | 'from webContents', | 221 | 'from webContents', |
@@ -224,7 +228,7 @@ async function createWindow(): Promise<unknown> { | |||
224 | 228 | ||
225 | ipcMain.on(RendererToMainIpcMessage.DispatchAction, (event, rawAction) => { | 229 | ipcMain.on(RendererToMainIpcMessage.DispatchAction, (event, rawAction) => { |
226 | if (event.sender.id !== webContents.id) { | 230 | if (event.sender.id !== webContents.id) { |
227 | console.warn( | 231 | log.warn( |
228 | 'Unexpected', | 232 | 'Unexpected', |
229 | RendererToMainIpcMessage.DispatchAction, | 233 | RendererToMainIpcMessage.DispatchAction, |
230 | 'from webContents', | 234 | 'from webContents', |
@@ -242,17 +246,26 @@ async function createWindow(): Promise<unknown> { | |||
242 | store.config.setThemeSource(actionToDispatch.themeSource); | 246 | store.config.setThemeSource(actionToDispatch.themeSource); |
243 | break; | 247 | break; |
244 | case 'reload-all-services': | 248 | case 'reload-all-services': |
245 | readFile(serviceInjectPath, 'utf8').then((data) => { | 249 | readFile(serviceInjectPath, 'utf8') |
246 | serviceInject.code = data; | 250 | .then((data) => { |
247 | }).catch((err) => { | 251 | serviceInject.code = data; |
248 | console.error('Error while reloading', serviceInjectPath, err); | 252 | }) |
249 | }).then(() => { | 253 | .catch((err) => { |
250 | browserView.webContents.reload(); | 254 | log.error('Error while reloading', serviceInjectPath, err); |
251 | }); | 255 | }) |
256 | .then(() => { | ||
257 | browserView.webContents.reload(); | ||
258 | }) | ||
259 | .catch((err) => { | ||
260 | log.error('Failed to reload browserView', err); | ||
261 | }); | ||
262 | break; | ||
263 | default: | ||
264 | log.error('Unexpected action from UI renderer:', actionToDispatch); | ||
252 | break; | 265 | break; |
253 | } | 266 | } |
254 | } catch (err) { | 267 | } catch (err) { |
255 | console.error('Error while dispatching renderer action', rawAction, err); | 268 | log.error('Error while dispatching renderer action', rawAction, err); |
256 | } | 269 | } |
257 | }); | 270 | }); |
258 | 271 | ||
@@ -260,11 +273,10 @@ async function createWindow(): Promise<unknown> { | |||
260 | webContents.send(MainToRendererIpcMessage.SharedStorePatch, patch); | 273 | webContents.send(MainToRendererIpcMessage.SharedStorePatch, patch); |
261 | }); | 274 | }); |
262 | 275 | ||
263 | ipcMain.handle(ServiceToMainIpcMessage.ApiExposedInMainWorld, (event) => { | 276 | ipcMain.handle( |
264 | return event.sender.id === browserView.webContents.id | 277 | ServiceToMainIpcMessage.ApiExposedInMainWorld, |
265 | ? serviceInject | 278 | (event) => (event.sender.id === browserView.webContents.id ? serviceInject : null), |
266 | : null; | 279 | ); |
267 | }); | ||
268 | 280 | ||
269 | browserView.webContents.on('ipc-message', (_event, channel, ...args) => { | 281 | browserView.webContents.on('ipc-message', (_event, channel, ...args) => { |
270 | try { | 282 | try { |
@@ -274,14 +286,14 @@ async function createWindow(): Promise<unknown> { | |||
274 | // otherwise electron emits a no handler registered warning. | 286 | // otherwise electron emits a no handler registered warning. |
275 | break; | 287 | break; |
276 | case ServiceToMainIpcMessage.SetUnreadCount: | 288 | case ServiceToMainIpcMessage.SetUnreadCount: |
277 | console.log('Unread count:', unreadCount.parse(args[0])); | 289 | log.log('Unread count:', unreadCount.parse(args[0])); |
278 | break; | 290 | break; |
279 | default: | 291 | default: |
280 | console.error('Unknown IPC message:', channel, args); | 292 | log.error('Unknown IPC message:', channel, args); |
281 | break; | 293 | break; |
282 | } | 294 | } |
283 | } catch (err) { | 295 | } catch (err) { |
284 | console.error('Error while processing IPC message:', channel, args, err); | 296 | log.error('Error while processing IPC message:', channel, args, err); |
285 | } | 297 | } |
286 | }); | 298 | }); |
287 | 299 | ||
@@ -291,17 +303,22 @@ async function createWindow(): Promise<unknown> { | |||
291 | }, | 303 | }, |
292 | ); | 304 | ); |
293 | 305 | ||
294 | browserView.webContents.session.webRequest.onBeforeSendHeaders(({ url, requestHeaders }, callback) => { | 306 | browserView.webContents.session.webRequest.onBeforeSendHeaders( |
295 | if (url.match(/^[^:]+:\/\/accounts\.google\.[^.\/]+\//)) { | 307 | ({ url, requestHeaders }, callback) => { |
296 | requestHeaders['User-Agent'] = chromelessUserAgent; | 308 | const requestUserAgent = url.match(/^[^:]+:\/\/accounts\.google\.[^./]+\//) |
297 | } else { | 309 | ? chromelessUserAgent |
298 | requestHeaders['User-Agent'] = userAgent; | 310 | : userAgent; |
299 | } | 311 | callback({ |
300 | callback({ requestHeaders }); | 312 | requestHeaders: { |
301 | }); | 313 | ...requestHeaders, |
314 | 'User-Agent': requestUserAgent, | ||
315 | }, | ||
316 | }); | ||
317 | }, | ||
318 | ); | ||
302 | 319 | ||
303 | browserView.webContents.loadURL('https://gitlab.com/say-hi-to-sophie/sophie').catch((err) => { | 320 | browserView.webContents.loadURL('https://gitlab.com/say-hi-to-sophie/sophie').catch((err) => { |
304 | console.error('Failed to load browser', err); | 321 | log.error('Failed to load browser', err); |
305 | }); | 322 | }); |
306 | 323 | ||
307 | return mainWindow.loadURL(pageUrl); | 324 | return mainWindow.loadURL(pageUrl); |
@@ -330,12 +347,12 @@ app.whenReady().then(async () => { | |||
330 | try { | 347 | try { |
331 | await installDevToolsExtensions(); | 348 | await installDevToolsExtensions(); |
332 | } catch (err) { | 349 | } catch (err) { |
333 | console.error('Failed to install devtools extensions', err); | 350 | log.error('Failed to install devtools extensions', err); |
334 | } | 351 | } |
335 | } | 352 | } |
336 | 353 | ||
337 | return createWindow(); | 354 | return createWindow(); |
338 | }).catch((err) => { | 355 | }).catch((err) => { |
339 | console.error('Failed to create window', err); | 356 | log.error('Failed to create window', err); |
340 | process.exit(1); | 357 | process.exit(1); |
341 | }); | 358 | }); |
diff --git a/packages/main/src/compositionRoot.ts b/packages/main/src/init.ts index 76835a1..4487cc4 100644 --- a/packages/main/src/compositionRoot.ts +++ b/packages/main/src/init.ts | |||
@@ -20,13 +20,13 @@ | |||
20 | 20 | ||
21 | import { app } from 'electron'; | 21 | import { app } from 'electron'; |
22 | 22 | ||
23 | import { initConfig } from './controllers/config'; | 23 | import initConfig from './controllers/initConfig'; |
24 | import { initNativeTheme } from './controllers/nativeTheme'; | 24 | import initNativeTheme from './controllers/initNativeTheme'; |
25 | import { ConfigPersistenceServiceImpl } from './services/impl/ConfigPersistenceServiceImpl'; | 25 | import ConfigPersistenceServiceImpl from './services/impl/ConfigPersistenceServiceImpl'; |
26 | import { MainStore } from './stores/MainStore'; | 26 | import { MainStore } from './stores/MainStore'; |
27 | import { Disposer } from './utils/disposer'; | 27 | import type Disposer from './utils/Disposer'; |
28 | 28 | ||
29 | export async function init(store: MainStore): Promise<Disposer> { | 29 | export default async function init(store: MainStore): Promise<Disposer> { |
30 | const configPersistenceService = new ConfigPersistenceServiceImpl(app.getPath('userData')); | 30 | const configPersistenceService = new ConfigPersistenceServiceImpl(app.getPath('userData')); |
31 | const disposeConfigController = await initConfig(store.config, configPersistenceService); | 31 | const disposeConfigController = await initConfig(store.config, configPersistenceService); |
32 | const disposeNativeThemeController = initNativeTheme(store); | 32 | const disposeNativeThemeController = initNativeTheme(store); |
diff --git a/packages/main/src/services/ConfigPersistenceService.ts b/packages/main/src/services/ConfigPersistenceService.ts index aed0ba3..7d508c5 100644 --- a/packages/main/src/services/ConfigPersistenceService.ts +++ b/packages/main/src/services/ConfigPersistenceService.ts | |||
@@ -18,12 +18,12 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import type { ConfigSnapshotOut } from '../stores/Config'; | 21 | import type { ConfigSnapshotOut } from '../stores/Config'; |
22 | import { Disposer } from '../utils/disposer'; | 22 | import type Disposer from '../utils/Disposer'; |
23 | 23 | ||
24 | export type ReadConfigResult = { found: true; data: unknown; } | { found: false; }; | 24 | export type ReadConfigResult = { found: true; data: unknown; } | { found: false; }; |
25 | 25 | ||
26 | export interface ConfigPersistenceService { | 26 | export default interface ConfigPersistenceService { |
27 | readConfig(): Promise<ReadConfigResult>; | 27 | readConfig(): Promise<ReadConfigResult>; |
28 | 28 | ||
29 | writeConfig(configSnapshot: ConfigSnapshotOut): Promise<void>; | 29 | writeConfig(configSnapshot: ConfigSnapshotOut): Promise<void>; |
diff --git a/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts b/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts index 2d19632..df8c807 100644 --- a/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts +++ b/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts | |||
@@ -19,18 +19,20 @@ | |||
19 | */ | 19 | */ |
20 | import { watch } from 'fs'; | 20 | import { watch } from 'fs'; |
21 | import { readFile, stat, writeFile } from 'fs/promises'; | 21 | import { readFile, stat, writeFile } from 'fs/promises'; |
22 | import JSON5 from 'json5'; | ||
23 | import { throttle } from 'lodash-es'; | ||
24 | import { join } from 'path'; | 22 | import { join } from 'path'; |
25 | 23 | ||
26 | import type { ConfigPersistenceService, ReadConfigResult } from '../ConfigPersistenceService.js'; | 24 | import JSON5 from 'json5'; |
27 | import type { ConfigSnapshotOut } from '../../stores/Config.js'; | 25 | import throttle from 'lodash-es/throttle'; |
28 | import { Disposer } from '../../utils/disposer'; | 26 | |
29 | import { getLogger } from '../../utils/logging'; | 27 | import type { ConfigSnapshotOut } from '../../stores/Config'; |
28 | import type Disposer from '../../utils/Disposer'; | ||
29 | import { getLogger } from '../../utils/log'; | ||
30 | import type ConfigPersistenceService from '../ConfigPersistenceService'; | ||
31 | import type { ReadConfigResult } from '../ConfigPersistenceService'; | ||
30 | 32 | ||
31 | const log = getLogger('configPersistence'); | 33 | const log = getLogger('configPersistence'); |
32 | 34 | ||
33 | export class ConfigPersistenceServiceImpl implements ConfigPersistenceService { | 35 | export default class ConfigPersistenceServiceImpl implements ConfigPersistenceService { |
34 | private readonly configFilePath: string; | 36 | private readonly configFilePath: string; |
35 | 37 | ||
36 | private writingConfig = false; | 38 | private writingConfig = false; |
@@ -103,7 +105,7 @@ export class ConfigPersistenceServiceImpl implements ConfigPersistenceService { | |||
103 | 'whish is newer than last written', | 105 | 'whish is newer than last written', |
104 | this.timeLastWritten, | 106 | this.timeLastWritten, |
105 | ); | 107 | ); |
106 | return callback(); | 108 | await callback(); |
107 | } | 109 | } |
108 | }, throttleMs); | 110 | }, throttleMs); |
109 | 111 | ||
@@ -115,7 +117,7 @@ export class ConfigPersistenceServiceImpl implements ConfigPersistenceService { | |||
115 | if (eventType === 'change' | 117 | if (eventType === 'change' |
116 | && (filename === this.configFileName || filename === null)) { | 118 | && (filename === this.configFileName || filename === null)) { |
117 | configChanged()?.catch((err) => { | 119 | configChanged()?.catch((err) => { |
118 | console.log('Unhandled error while listening for config changes', err); | 120 | log.error('Unhandled error while listening for config changes', err); |
119 | }); | 121 | }); |
120 | } | 122 | } |
121 | }); | 123 | }); |
diff --git a/packages/main/src/stores/Config.ts b/packages/main/src/stores/Config.ts index 7d1168f..06dbdeb 100644 --- a/packages/main/src/stores/Config.ts +++ b/packages/main/src/stores/Config.ts | |||
@@ -18,13 +18,13 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { Instance } from 'mobx-state-tree'; | ||
22 | import { | 21 | import { |
23 | config as originalConfig, | 22 | config as originalConfig, |
24 | ConfigSnapshotIn, | 23 | ConfigSnapshotIn, |
25 | ConfigSnapshotOut, | 24 | ConfigSnapshotOut, |
26 | ThemeSource, | 25 | ThemeSource, |
27 | } from '@sophie/shared'; | 26 | } from '@sophie/shared'; |
27 | import { Instance } from 'mobx-state-tree'; | ||
28 | 28 | ||
29 | export const config = originalConfig.actions((self) => ({ | 29 | export const config = originalConfig.actions((self) => ({ |
30 | setThemeSource(mode: ThemeSource) { | 30 | setThemeSource(mode: ThemeSource) { |
diff --git a/packages/main/src/stores/MainStore.ts b/packages/main/src/stores/MainStore.ts index f8a09d6..7b26c52 100644 --- a/packages/main/src/stores/MainStore.ts +++ b/packages/main/src/stores/MainStore.ts | |||
@@ -18,8 +18,8 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { applySnapshot, Instance, types } from 'mobx-state-tree'; | ||
22 | import { BrowserViewBounds } from '@sophie/shared'; | 21 | import { BrowserViewBounds } from '@sophie/shared'; |
22 | import { applySnapshot, Instance, types } from 'mobx-state-tree'; | ||
23 | 23 | ||
24 | import type { Config } from './Config.js'; | 24 | import type { Config } from './Config.js'; |
25 | import { sharedStore } from './SharedStore'; | 25 | import { sharedStore } from './SharedStore'; |
diff --git a/packages/main/src/stores/SharedStore.ts b/packages/main/src/stores/SharedStore.ts index e20150d..c023fc7 100644 --- a/packages/main/src/stores/SharedStore.ts +++ b/packages/main/src/stores/SharedStore.ts | |||
@@ -18,8 +18,8 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { Instance, types } from 'mobx-state-tree'; | ||
22 | import { sharedStore as originalSharedStore } from '@sophie/shared'; | 21 | import { sharedStore as originalSharedStore } from '@sophie/shared'; |
22 | import { Instance, types } from 'mobx-state-tree'; | ||
23 | 23 | ||
24 | import { config } from './Config'; | 24 | import { config } from './Config'; |
25 | 25 | ||
diff --git a/packages/main/src/utils/disposer.ts b/packages/main/src/utils/Disposer.ts index 0d469dd..2e0ca25 100644 --- a/packages/main/src/utils/disposer.ts +++ b/packages/main/src/utils/Disposer.ts | |||
@@ -20,4 +20,6 @@ | |||
20 | 20 | ||
21 | import { IDisposer } from 'mobx-state-tree'; | 21 | import { IDisposer } from 'mobx-state-tree'; |
22 | 22 | ||
23 | export type Disposer = IDisposer; | 23 | type Disposer = IDisposer; |
24 | |||
25 | export default Disposer; | ||
diff --git a/packages/main/src/utils/logging.ts b/packages/main/src/utils/log.ts index f703749..c704797 100644 --- a/packages/main/src/utils/logging.ts +++ b/packages/main/src/utils/log.ts | |||
@@ -45,7 +45,11 @@ prefix.reg(loglevel); | |||
45 | prefix.apply(loglevel, { | 45 | prefix.apply(loglevel, { |
46 | format(level, name, timestamp) { | 46 | format(level, name, timestamp) { |
47 | const levelColor = getColor(level); | 47 | const levelColor = getColor(level); |
48 | return `${chalk.gray(`[${timestamp}]`)} ${levelColor(level)} ${chalk.green(`${name}:`)}`; | 48 | const timeStr = timestamp.toString(); |
49 | const nameStr = typeof name === 'undefined' | ||
50 | ? levelColor(':') | ||
51 | : ` ${chalk.green(`${name}:`)}`; | ||
52 | return `${chalk.gray(`[${timeStr}]`)} ${levelColor(level)}${nameStr}`; | ||
49 | }, | 53 | }, |
50 | }); | 54 | }); |
51 | 55 | ||
@@ -56,7 +60,7 @@ export function getLogger(loggerName: string): Logger { | |||
56 | export function silenceLogger(): void { | 60 | export function silenceLogger(): void { |
57 | loglevel.disableAll(); | 61 | loglevel.disableAll(); |
58 | const loggers = loglevel.getLoggers(); | 62 | const loggers = loglevel.getLoggers(); |
59 | for (const loggerName of Object.keys(loggers)) { | 63 | Object.keys(loggers).forEach((loggerName) => { |
60 | loggers[loggerName].disableAll(); | 64 | loggers[loggerName].disableAll(); |
61 | } | 65 | }); |
62 | } | 66 | } |
diff --git a/packages/main/tsconfig.json b/packages/main/tsconfig.json index 1401445..00a1985 100644 --- a/packages/main/tsconfig.json +++ b/packages/main/tsconfig.json | |||
@@ -1,5 +1,5 @@ | |||
1 | { | 1 | { |
2 | "extends": "../../tsconfig.json", | 2 | "extends": "../../config/tsconfig.base.json", |
3 | "compilerOptions": { | 3 | "compilerOptions": { |
4 | "noEmit": true, | 4 | "noEmit": true, |
5 | "types": [ | 5 | "types": [ |
@@ -9,14 +9,16 @@ | |||
9 | }, | 9 | }, |
10 | "references": [ | 10 | "references": [ |
11 | { | 11 | { |
12 | "path": "../service-shared" | 12 | "path": "../service-shared/tsconfig.build.json" |
13 | }, | 13 | }, |
14 | { | 14 | { |
15 | "path": "../shared" | 15 | "path": "../shared/tsconfig.build.json" |
16 | } | 16 | } |
17 | ], | 17 | ], |
18 | "include": [ | 18 | "include": [ |
19 | "src/**/*.ts", | 19 | "src/**/*.ts", |
20 | "types/**/*.d.ts" | 20 | "types/**/*.d.ts", |
21 | "esbuild.config.js", | ||
22 | "jest.config.js" | ||
21 | ] | 23 | ] |
22 | } | 24 | } |
diff --git a/packages/preload/.eslintrc.cjs b/packages/preload/.eslintrc.cjs new file mode 100644 index 0000000..02fab21 --- /dev/null +++ b/packages/preload/.eslintrc.cjs | |||
@@ -0,0 +1,6 @@ | |||
1 | module.exports = { | ||
2 | env: { | ||
3 | node: true, | ||
4 | browser: true, | ||
5 | }, | ||
6 | }; | ||
diff --git a/packages/preload/esbuild.config.js b/packages/preload/esbuild.config.js index b73a071..66f5e84 100644 --- a/packages/preload/esbuild.config.js +++ b/packages/preload/esbuild.config.js | |||
@@ -1,8 +1,8 @@ | |||
1 | import { chrome } from '../../config/buildConstants.js'; | 1 | import { chrome } from '../../config/buildConstants.js'; |
2 | import { getConfig } from '../../config/esbuildConfig.js'; | 2 | import fileURLToDirname from '../../config/fileURLToDirname.js'; |
3 | import { fileURLToDirname } from '../../config/utils.js'; | 3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; |
4 | 4 | ||
5 | export default getConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileURLToDirname(import.meta.url), |
7 | entryPoints: [ | 7 | entryPoints: [ |
8 | 'src/index.ts', | 8 | 'src/index.ts', |
diff --git a/packages/preload/jest.config.js b/packages/preload/jest.config.js index e474c4c..27af475 100644 --- a/packages/preload/jest.config.js +++ b/packages/preload/jest.config.js | |||
@@ -1,6 +1,6 @@ | |||
1 | import rootConfig from '../../config/jest.config.base.js'; | 1 | import rootConfig from '../../config/jest.config.base.js'; |
2 | 2 | ||
3 | /** @type {import('ts-jest').InitialOptionsTsJest} */ | 3 | /** @type {import('@jest/types').Config.InitialOptions} */ |
4 | export default { | 4 | export default { |
5 | ...rootConfig, | 5 | ...rootConfig, |
6 | testEnvironment: 'jsdom', | 6 | testEnvironment: 'jsdom', |
diff --git a/packages/preload/package.json b/packages/preload/package.json index 0957aaf..a03d7d9 100644 --- a/packages/preload/package.json +++ b/packages/preload/package.json | |||
@@ -6,7 +6,7 @@ | |||
6 | "type": "module", | 6 | "type": "module", |
7 | "types": "dist-types/index.d.ts", | 7 | "types": "dist-types/index.d.ts", |
8 | "scripts": { | 8 | "scripts": { |
9 | "typecheck": "tsc" | 9 | "typecheck:workspace": "yarn g:typecheck" |
10 | }, | 10 | }, |
11 | "dependencies": { | 11 | "dependencies": { |
12 | "@sophie/shared": "workspace:*", | 12 | "@sophie/shared": "workspace:*", |
@@ -20,8 +20,6 @@ | |||
20 | "@types/jest": "^27.4.0", | 20 | "@types/jest": "^27.4.0", |
21 | "jest": "^27.4.7", | 21 | "jest": "^27.4.7", |
22 | "jest-mock": "^27.4.6", | 22 | "jest-mock": "^27.4.6", |
23 | "jsdom": "^19.0.0", | 23 | "jsdom": "^19.0.0" |
24 | "rimraf": "^3.0.2", | ||
25 | "typescript": "^4.5.4" | ||
26 | } | 24 | } |
27 | } | 25 | } |
diff --git a/packages/preload/src/contextBridge/__tests__/SophieRendererImpl.spec.ts b/packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts index ff77a63..a38dbac 100644 --- a/packages/preload/src/contextBridge/__tests__/SophieRendererImpl.spec.ts +++ b/packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts | |||
@@ -19,9 +19,6 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { jest } from '@jest/globals'; | 21 | import { jest } from '@jest/globals'; |
22 | import { mocked } from 'jest-mock'; | ||
23 | import log from 'loglevel'; | ||
24 | import type { IJsonPatch } from 'mobx-state-tree'; | ||
25 | import { | 22 | import { |
26 | Action, | 23 | Action, |
27 | MainToRendererIpcMessage, | 24 | MainToRendererIpcMessage, |
@@ -29,6 +26,9 @@ import { | |||
29 | SharedStoreSnapshotIn, | 26 | SharedStoreSnapshotIn, |
30 | SophieRenderer, | 27 | SophieRenderer, |
31 | } from '@sophie/shared'; | 28 | } from '@sophie/shared'; |
29 | import { mocked } from 'jest-mock'; | ||
30 | import log from 'loglevel'; | ||
31 | import type { IJsonPatch } from 'mobx-state-tree'; | ||
32 | 32 | ||
33 | jest.unstable_mockModule('electron', () => ({ | 33 | jest.unstable_mockModule('electron', () => ({ |
34 | ipcRenderer: { | 34 | ipcRenderer: { |
@@ -40,7 +40,7 @@ jest.unstable_mockModule('electron', () => ({ | |||
40 | 40 | ||
41 | const { ipcRenderer } = await import('electron'); | 41 | const { ipcRenderer } = await import('electron'); |
42 | 42 | ||
43 | const { createSophieRenderer } = await import('../SophieRendererImpl'); | 43 | const { default: createSophieRenderer } = await import('../createSophieRenderer'); |
44 | 44 | ||
45 | const event: Electron.IpcRendererEvent = null as unknown as Electron.IpcRendererEvent; | 45 | const event: Electron.IpcRendererEvent = null as unknown as Electron.IpcRendererEvent; |
46 | 46 | ||
@@ -81,10 +81,10 @@ describe('createSophieRenderer', () => { | |||
81 | }); | 81 | }); |
82 | }); | 82 | }); |
83 | 83 | ||
84 | describe('SophieRendererImpl', () => { | 84 | describe('SharedStoreConnector', () => { |
85 | let sut: SophieRenderer; | 85 | let sut: SophieRenderer; |
86 | let onSharedStorePatch: (event1: Electron.IpcRendererEvent, patch1: unknown) => void; | 86 | let onSharedStorePatch: (eventArg: Electron.IpcRendererEvent, patchArg: unknown) => void; |
87 | let listener = { | 87 | const listener = { |
88 | // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars | 88 | // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars |
89 | onSnapshot: jest.fn((_snapshot: SharedStoreSnapshotIn) => {}), | 89 | onSnapshot: jest.fn((_snapshot: SharedStoreSnapshotIn) => {}), |
90 | // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars | 90 | // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars |
@@ -93,9 +93,9 @@ describe('SophieRendererImpl', () => { | |||
93 | 93 | ||
94 | beforeEach(() => { | 94 | beforeEach(() => { |
95 | sut = createSophieRenderer(false); | 95 | sut = createSophieRenderer(false); |
96 | onSharedStorePatch = mocked(ipcRenderer.on).mock.calls.find(([channel]) => { | 96 | [, onSharedStorePatch] = mocked(ipcRenderer.on).mock.calls.find( |
97 | return channel === MainToRendererIpcMessage.SharedStorePatch; | 97 | ([channel]) => channel === MainToRendererIpcMessage.SharedStorePatch, |
98 | })?.[1]!; | 98 | )!; |
99 | }); | 99 | }); |
100 | 100 | ||
101 | describe('onSharedStoreChange', () => { | 101 | describe('onSharedStoreChange', () => { |
@@ -140,15 +140,15 @@ describe('SophieRendererImpl', () => { | |||
140 | }); | 140 | }); |
141 | }); | 141 | }); |
142 | 142 | ||
143 | function itRefusesToRegisterAnotherListener() { | 143 | function itRefusesToRegisterAnotherListener(): void { |
144 | it('should refuse to register another listener', async () => { | 144 | it('should refuse to register another listener', async () => { |
145 | await expect(sut.onSharedStoreChange(listener)).rejects.toBeInstanceOf(Error); | 145 | await expect(sut.onSharedStoreChange(listener)).rejects.toBeInstanceOf(Error); |
146 | }); | 146 | }); |
147 | } | 147 | } |
148 | 148 | ||
149 | function itDoesNotPassPatchesToTheListener( | 149 | function itDoesNotPassPatchesToTheListener( |
150 | name: string = 'should not pass patches to the listener', | 150 | name = 'should not pass patches to the listener', |
151 | ) { | 151 | ): void { |
152 | it(name, () => { | 152 | it(name, () => { |
153 | onSharedStorePatch(event, patch); | 153 | onSharedStorePatch(event, patch); |
154 | expect(listener.onPatch).not.toBeCalled(); | 154 | expect(listener.onPatch).not.toBeCalled(); |
diff --git a/packages/preload/src/contextBridge/SophieRendererImpl.ts b/packages/preload/src/contextBridge/createSophieRenderer.ts index f3c07c5..2055080 100644 --- a/packages/preload/src/contextBridge/SophieRendererImpl.ts +++ b/packages/preload/src/contextBridge/createSophieRenderer.ts | |||
@@ -18,9 +18,6 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { ipcRenderer } from 'electron'; | ||
22 | import log from 'loglevel'; | ||
23 | import type { IJsonPatch } from 'mobx-state-tree'; | ||
24 | import { | 21 | import { |
25 | Action, | 22 | Action, |
26 | action, | 23 | action, |
@@ -30,9 +27,12 @@ import { | |||
30 | SharedStoreListener, | 27 | SharedStoreListener, |
31 | SophieRenderer, | 28 | SophieRenderer, |
32 | } from '@sophie/shared'; | 29 | } from '@sophie/shared'; |
30 | import { ipcRenderer } from 'electron'; | ||
31 | import log from 'loglevel'; | ||
32 | import type { IJsonPatch } from 'mobx-state-tree'; | ||
33 | 33 | ||
34 | class SophieRendererImpl implements SophieRenderer { | 34 | class SharedStoreConnector { |
35 | private onSharedStoreChangeCalled: boolean = false; | 35 | private onSharedStoreChangeCalled = false; |
36 | 36 | ||
37 | private listener: SharedStoreListener | null = null; | 37 | private listener: SharedStoreListener | null = null; |
38 | 38 | ||
@@ -71,26 +71,26 @@ class SophieRendererImpl implements SophieRenderer { | |||
71 | } | 71 | } |
72 | throw new Error('Failed to connect to shared store'); | 72 | throw new Error('Failed to connect to shared store'); |
73 | } | 73 | } |
74 | } | ||
74 | 75 | ||
75 | dispatchAction(actionToDispatch: Action): void { | 76 | function dispatchAction(actionToDispatch: Action): void { |
76 | // Let the full zod parse error bubble up to the main world, | 77 | // Let the full zod parse error bubble up to the main world, |
77 | // since all data it may contain was provided from the main world. | 78 | // since all data it may contain was provided from the main world. |
78 | const parsedAction = action.parse(actionToDispatch); | 79 | const parsedAction = action.parse(actionToDispatch); |
79 | try { | 80 | try { |
80 | ipcRenderer.send(RendererToMainIpcMessage.DispatchAction, parsedAction); | 81 | ipcRenderer.send(RendererToMainIpcMessage.DispatchAction, parsedAction); |
81 | } catch (err) { | 82 | } catch (err) { |
82 | // Do not leak IPC failure details into the main world. | 83 | // Do not leak IPC failure details into the main world. |
83 | const message = 'Failed to dispatch action'; | 84 | const message = 'Failed to dispatch action'; |
84 | log.error(message, actionToDispatch, err); | 85 | log.error(message, actionToDispatch, err); |
85 | throw new Error(message); | 86 | throw new Error(message); |
86 | } | ||
87 | } | 87 | } |
88 | } | 88 | } |
89 | 89 | ||
90 | export function createSophieRenderer(allowReplaceListener: boolean): SophieRenderer { | 90 | export default function createSophieRenderer(allowReplaceListener: boolean): SophieRenderer { |
91 | const impl = new SophieRendererImpl(allowReplaceListener); | 91 | const connector = new SharedStoreConnector(allowReplaceListener); |
92 | return { | 92 | return { |
93 | onSharedStoreChange: impl.onSharedStoreChange.bind(impl), | 93 | onSharedStoreChange: connector.onSharedStoreChange.bind(connector), |
94 | dispatchAction: impl.dispatchAction.bind(impl), | 94 | dispatchAction, |
95 | }; | 95 | }; |
96 | } | 96 | } |
diff --git a/packages/preload/src/index.ts b/packages/preload/src/index.ts index de91742..f13220c 100644 --- a/packages/preload/src/index.ts +++ b/packages/preload/src/index.ts | |||
@@ -20,7 +20,7 @@ | |||
20 | 20 | ||
21 | import { contextBridge } from 'electron'; | 21 | import { contextBridge } from 'electron'; |
22 | 22 | ||
23 | import { createSophieRenderer } from './contextBridge/SophieRendererImpl'; | 23 | import createSophieRenderer from './contextBridge/createSophieRenderer'; |
24 | 24 | ||
25 | const isDevelopment = import.meta.env.MODE === 'development'; | 25 | const isDevelopment = import.meta.env.MODE === 'development'; |
26 | 26 | ||
diff --git a/packages/preload/tsconfig.json b/packages/preload/tsconfig.json index 741d435..ff49538 100644 --- a/packages/preload/tsconfig.json +++ b/packages/preload/tsconfig.json | |||
@@ -1,5 +1,5 @@ | |||
1 | { | 1 | { |
2 | "extends": "../../tsconfig.json", | 2 | "extends": "../../config/tsconfig.base.json", |
3 | "compilerOptions": { | 3 | "compilerOptions": { |
4 | "noEmit": true, | 4 | "noEmit": true, |
5 | "lib": [ | 5 | "lib": [ |
@@ -13,11 +13,13 @@ | |||
13 | }, | 13 | }, |
14 | "references": [ | 14 | "references": [ |
15 | { | 15 | { |
16 | "path": "../shared" | 16 | "path": "../shared/tsconfig.build.json" |
17 | } | 17 | } |
18 | ], | 18 | ], |
19 | "include": [ | 19 | "include": [ |
20 | "src/**/*.ts", | 20 | "src/**/*.ts", |
21 | "types/**/*.d.ts" | 21 | "types/**/*.d.ts", |
22 | "esbuild.config.js", | ||
23 | "jest.config.js" | ||
22 | ] | 24 | ] |
23 | } | 25 | } |
diff --git a/packages/renderer/.eslinrc.cjs b/packages/renderer/.eslinrc.cjs new file mode 100644 index 0000000..3385ac5 --- /dev/null +++ b/packages/renderer/.eslinrc.cjs | |||
@@ -0,0 +1,11 @@ | |||
1 | module.exports = { | ||
2 | extends: [ | ||
3 | 'airbnb', | ||
4 | 'airbnb/hooks', | ||
5 | 'airbnb-typescript', | ||
6 | ], | ||
7 | env: { | ||
8 | node: false, | ||
9 | browser: true, | ||
10 | }, | ||
11 | }; | ||
diff --git a/packages/renderer/.eslintrc.json b/packages/renderer/.eslintrc.json deleted file mode 100644 index a28aec9..0000000 --- a/packages/renderer/.eslintrc.json +++ /dev/null | |||
@@ -1,5 +0,0 @@ | |||
1 | { | ||
2 | "globals": { | ||
3 | "JSX": false | ||
4 | } | ||
5 | } | ||
diff --git a/packages/renderer/package.json b/packages/renderer/package.json index df15abb..fde4c28 100644 --- a/packages/renderer/package.json +++ b/packages/renderer/package.json | |||
@@ -5,7 +5,7 @@ | |||
5 | "type": "module", | 5 | "type": "module", |
6 | "types": "dist-types/index.d.ts", | 6 | "types": "dist-types/index.d.ts", |
7 | "scripts": { | 7 | "scripts": { |
8 | "typecheck": "tsc" | 8 | "typecheck:workspace": "yarn g:typecheck" |
9 | }, | 9 | }, |
10 | "dependencies": { | 10 | "dependencies": { |
11 | "@emotion/react": "^11.7.1", | 11 | "@emotion/react": "^11.7.1", |
@@ -14,7 +14,9 @@ | |||
14 | "@mui/icons-material": "^5.2.5", | 14 | "@mui/icons-material": "^5.2.5", |
15 | "@mui/material": "^5.2.7", | 15 | "@mui/material": "^5.2.7", |
16 | "@sophie/shared": "workspace:*", | 16 | "@sophie/shared": "workspace:*", |
17 | "lodash": "^4.17.21", | 17 | "lodash-es": "^4.17.21", |
18 | "loglevel": "^1.8.0", | ||
19 | "loglevel-plugin-prefix": "^0.8.4", | ||
18 | "mobx": "^6.3.12", | 20 | "mobx": "^6.3.12", |
19 | "mobx-react-lite": "^3.2.3", | 21 | "mobx-react-lite": "^3.2.3", |
20 | "mobx-state-tree": "^5.1.0", | 22 | "mobx-state-tree": "^5.1.0", |
@@ -22,14 +24,12 @@ | |||
22 | "react-dom": "^17.0.2" | 24 | "react-dom": "^17.0.2" |
23 | }, | 25 | }, |
24 | "devDependencies": { | 26 | "devDependencies": { |
25 | "@types/lodash": "^4.14.178", | 27 | "@types/lodash-es": "^4.14.178", |
26 | "@types/react": "^17.0.38", | 28 | "@types/react": "^17.0.38", |
27 | "@types/react-dom": "^17.0.11", | 29 | "@types/react-dom": "^17.0.11", |
28 | "@vitejs/plugin-react": "^1.1.4", | 30 | "@vitejs/plugin-react": "^1.1.4", |
29 | "mst-middlewares": "^5.1.0", | 31 | "mst-middlewares": "^5.1.0", |
30 | "remotedev": "^0.2.9", | 32 | "remotedev": "^0.2.9", |
31 | "rimraf": "^3.0.2", | ||
32 | "typescript": "^4.5.4", | ||
33 | "vite": "^2.7.10" | 33 | "vite": "^2.7.10" |
34 | } | 34 | } |
35 | } | 35 | } |
diff --git a/packages/renderer/src/components/App.tsx b/packages/renderer/src/components/App.tsx index 8bd3dd8..1174bbb 100644 --- a/packages/renderer/src/components/App.tsx +++ b/packages/renderer/src/components/App.tsx | |||
@@ -21,10 +21,10 @@ | |||
21 | import Box from '@mui/material/Box'; | 21 | import Box from '@mui/material/Box'; |
22 | import React from 'react'; | 22 | import React from 'react'; |
23 | 23 | ||
24 | import { BrowserViewPlaceholder } from './BrowserViewPlaceholder'; | 24 | import BrowserViewPlaceholder from './BrowserViewPlaceholder'; |
25 | import { Sidebar } from './Sidebar'; | 25 | import Sidebar from './Sidebar'; |
26 | 26 | ||
27 | export function App(): JSX.Element { | 27 | export default function App(): JSX.Element { |
28 | return ( | 28 | return ( |
29 | <Box | 29 | <Box |
30 | sx={{ | 30 | sx={{ |
diff --git a/packages/renderer/src/components/BrowserViewPlaceholder.tsx b/packages/renderer/src/components/BrowserViewPlaceholder.tsx index 6aa6b7b..c671983 100644 --- a/packages/renderer/src/components/BrowserViewPlaceholder.tsx +++ b/packages/renderer/src/components/BrowserViewPlaceholder.tsx | |||
@@ -18,17 +18,15 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { throttle } from 'lodash'; | ||
22 | import { observer } from 'mobx-react-lite'; | ||
23 | import Box from '@mui/material/Box'; | 21 | import Box from '@mui/material/Box'; |
22 | import throttle from 'lodash-es/throttle'; | ||
23 | import { observer } from 'mobx-react-lite'; | ||
24 | import React, { useCallback, useRef } from 'react'; | 24 | import React, { useCallback, useRef } from 'react'; |
25 | 25 | ||
26 | import { useStore } from './StoreProvider'; | 26 | import { useStore } from './StoreProvider'; |
27 | 27 | ||
28 | export const BrowserViewPlaceholder = observer(function BrowserViewPlaceholder() { | 28 | export default observer(() => { |
29 | const { | 29 | const store = useStore(); |
30 | setBrowserViewBounds, | ||
31 | } = useStore(); | ||
32 | 30 | ||
33 | const onResize = useCallback(throttle(([entry]: ResizeObserverEntry[]) => { | 31 | const onResize = useCallback(throttle(([entry]: ResizeObserverEntry[]) => { |
34 | if (entry) { | 32 | if (entry) { |
@@ -38,14 +36,14 @@ export const BrowserViewPlaceholder = observer(function BrowserViewPlaceholder() | |||
38 | width, | 36 | width, |
39 | height, | 37 | height, |
40 | } = entry.target.getBoundingClientRect(); | 38 | } = entry.target.getBoundingClientRect(); |
41 | setBrowserViewBounds({ | 39 | store.setBrowserViewBounds({ |
42 | x, | 40 | x, |
43 | y, | 41 | y, |
44 | width, | 42 | width, |
45 | height, | 43 | height, |
46 | }); | 44 | }); |
47 | } | 45 | } |
48 | }, 100), [setBrowserViewBounds]); | 46 | }, 100), [store]); |
49 | 47 | ||
50 | const resizeObserverRef = useRef<ResizeObserver | null>(null); | 48 | const resizeObserverRef = useRef<ResizeObserver | null>(null); |
51 | 49 | ||
diff --git a/packages/renderer/src/components/Sidebar.tsx b/packages/renderer/src/components/Sidebar.tsx index 6c79932..44a47b0 100644 --- a/packages/renderer/src/components/Sidebar.tsx +++ b/packages/renderer/src/components/Sidebar.tsx | |||
@@ -21,9 +21,9 @@ | |||
21 | import Box from '@mui/material/Box'; | 21 | import Box from '@mui/material/Box'; |
22 | import React from 'react'; | 22 | import React from 'react'; |
23 | 23 | ||
24 | import { ToggleDarkModeButton } from './ToggleDarkModeButton'; | 24 | import ToggleDarkModeButton from './ToggleDarkModeButton'; |
25 | 25 | ||
26 | export function Sidebar(): JSX.Element { | 26 | export default function Sidebar(): JSX.Element { |
27 | return ( | 27 | return ( |
28 | <Box | 28 | <Box |
29 | sx={(theme) => ({ | 29 | sx={(theme) => ({ |
diff --git a/packages/renderer/src/components/StoreProvider.tsx b/packages/renderer/src/components/StoreProvider.tsx index da1e699..cde6a31 100644 --- a/packages/renderer/src/components/StoreProvider.tsx +++ b/packages/renderer/src/components/StoreProvider.tsx | |||
@@ -32,7 +32,7 @@ export function useStore(): RendererStore { | |||
32 | return store; | 32 | return store; |
33 | } | 33 | } |
34 | 34 | ||
35 | export function StoreProvider({ children, store }: { | 35 | export default function StoreProvider({ children, store }: { |
36 | children: JSX.Element | JSX.Element[], | 36 | children: JSX.Element | JSX.Element[], |
37 | store: RendererStore, | 37 | store: RendererStore, |
38 | }): JSX.Element { | 38 | }): JSX.Element { |
diff --git a/packages/renderer/src/components/ThemeProvider.tsx b/packages/renderer/src/components/ThemeProvider.tsx index 9215f5c..eacaa52 100644 --- a/packages/renderer/src/components/ThemeProvider.tsx +++ b/packages/renderer/src/components/ThemeProvider.tsx | |||
@@ -18,18 +18,18 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { observer } from 'mobx-react-lite'; | ||
22 | import { | 21 | import { |
23 | unstable_createMuiStrictModeTheme as createTheme, | 22 | unstable_createMuiStrictModeTheme as createTheme, |
24 | ThemeProvider as MuiThemeProvider, | 23 | ThemeProvider as MuiThemeProvider, |
25 | } from '@mui/material/styles'; | 24 | } from '@mui/material/styles'; |
25 | import { observer } from 'mobx-react-lite'; | ||
26 | import React from 'react'; | 26 | import React from 'react'; |
27 | 27 | ||
28 | import { useStore } from './StoreProvider'; | 28 | import { useStore } from './StoreProvider'; |
29 | 29 | ||
30 | export const ThemeProvider = observer(function ThemeProvider({ children }: { | 30 | export default observer(({ children }: { |
31 | children: JSX.Element | JSX.Element[], | 31 | children: JSX.Element | JSX.Element[]; |
32 | }) { | 32 | }) => { |
33 | const { shared: { shouldUseDarkColors } } = useStore(); | 33 | const { shared: { shouldUseDarkColors } } = useStore(); |
34 | 34 | ||
35 | const theme = createTheme({ | 35 | const theme = createTheme({ |
diff --git a/packages/renderer/src/components/ToggleDarkModeButton.tsx b/packages/renderer/src/components/ToggleDarkModeButton.tsx index 1b6757e..c8ffdf0 100644 --- a/packages/renderer/src/components/ToggleDarkModeButton.tsx +++ b/packages/renderer/src/components/ToggleDarkModeButton.tsx | |||
@@ -18,21 +18,22 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { observer } from 'mobx-react-lite'; | ||
22 | import DarkModeIcon from '@mui/icons-material/DarkMode'; | 21 | import DarkModeIcon from '@mui/icons-material/DarkMode'; |
23 | import LightModeIcon from '@mui/icons-material/LightMode'; | 22 | import LightModeIcon from '@mui/icons-material/LightMode'; |
24 | import IconButton from '@mui/material/IconButton'; | 23 | import IconButton from '@mui/material/IconButton'; |
24 | import { observer } from 'mobx-react-lite'; | ||
25 | import React from 'react'; | 25 | import React from 'react'; |
26 | 26 | ||
27 | import { useStore } from './StoreProvider'; | 27 | import { useStore } from './StoreProvider'; |
28 | 28 | ||
29 | export const ToggleDarkModeButton = observer(function ToggleDarkModeButton() { | 29 | export default observer(() => { |
30 | const { shared: { shouldUseDarkColors }, toggleDarkMode } = useStore(); | 30 | const store = useStore(); |
31 | const { shared: { shouldUseDarkColors } } = store; | ||
31 | 32 | ||
32 | return ( | 33 | return ( |
33 | <IconButton | 34 | <IconButton |
34 | aria-label="Toggle dark mode" | 35 | aria-label="Toggle dark mode" |
35 | onClick={() => toggleDarkMode()} | 36 | onClick={() => store.toggleDarkMode()} |
36 | > | 37 | > |
37 | {shouldUseDarkColors ? <LightModeIcon /> : <DarkModeIcon />} | 38 | {shouldUseDarkColors ? <LightModeIcon /> : <DarkModeIcon />} |
38 | </IconButton> | 39 | </IconButton> |
diff --git a/packages/renderer/src/devTools.ts b/packages/renderer/src/devTools.ts index 3ec66aa..3d3ba99 100644 --- a/packages/renderer/src/devTools.ts +++ b/packages/renderer/src/devTools.ts | |||
@@ -32,31 +32,24 @@ import type { IAnyStateTreeNode } from 'mobx-state-tree'; | |||
32 | * However, we don't bundle `remotedev` in production, so the call would fail anyways. | 32 | * However, we don't bundle `remotedev` in production, so the call would fail anyways. |
33 | * | 33 | * |
34 | * @param model The store to connect to the redux devtools. | 34 | * @param model The store to connect to the redux devtools. |
35 | * @return A promise that resolves when the store was exposed to the devtools. | ||
35 | * @see https://github.com/SocketCluster/socketcluster-client/issues/118#issuecomment-469064682 | 36 | * @see https://github.com/SocketCluster/socketcluster-client/issues/118#issuecomment-469064682 |
36 | */ | 37 | */ |
37 | async function exposeToReduxDevtoolsAsync(model: IAnyStateTreeNode): Promise<void> { | 38 | export async function exposeToReduxDevtools(model: IAnyStateTreeNode): Promise<void> { |
38 | (window as { global?: unknown }).global = window; | 39 | (window as { global?: unknown }).global = window; |
39 | 40 | ||
41 | // Hack to load dev dependencies on demand. | ||
40 | const [remotedev, { connectReduxDevtools }] = await Promise.all([ | 42 | const [remotedev, { connectReduxDevtools }] = await Promise.all([ |
41 | // @ts-ignore | 43 | // @ts-expect-error `remotedev` has no typings. |
42 | import('remotedev'), | 44 | // eslint-disable-next-line import/no-extraneous-dependencies |
45 | import('remotedev') as unknown, | ||
46 | // eslint-disable-next-line import/no-extraneous-dependencies | ||
43 | import('mst-middlewares'), | 47 | import('mst-middlewares'), |
44 | ]); | 48 | ]); |
45 | connectReduxDevtools(remotedev, model); | 49 | connectReduxDevtools(remotedev, model); |
46 | } | 50 | } |
47 | 51 | ||
48 | /** | 52 | /** |
49 | * Connects the `model` to the redux devtools extension. | ||
50 | * | ||
51 | * @param model The store to connect to the redux devtools. | ||
52 | */ | ||
53 | export function exposeToReduxDevtools(model: IAnyStateTreeNode): void { | ||
54 | exposeToReduxDevtoolsAsync(model).catch((err) => { | ||
55 | console.error('Could not connect to Redux devtools', err); | ||
56 | }); | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * Sends a message to the main process to reload all services when | 53 | * Sends a message to the main process to reload all services when |
61 | * `build/watch.js` sends a reload event on bundle write. | 54 | * `build/watch.js` sends a reload event on bundle write. |
62 | */ | 55 | */ |
diff --git a/packages/renderer/src/index.tsx b/packages/renderer/src/index.tsx index 1626bef..d900e50 100644 --- a/packages/renderer/src/index.tsx +++ b/packages/renderer/src/index.tsx | |||
@@ -26,14 +26,17 @@ import CssBaseline from '@mui/material/CssBaseline'; | |||
26 | import React from 'react'; | 26 | import React from 'react'; |
27 | import { render } from 'react-dom'; | 27 | import { render } from 'react-dom'; |
28 | 28 | ||
29 | import { App } from './components/App'; | 29 | import App from './components/App'; |
30 | import { StoreProvider } from './components/StoreProvider'; | 30 | import StoreProvider from './components/StoreProvider'; |
31 | import { ThemeProvider } from './components/ThemeProvider'; | 31 | import ThemeProvider from './components/ThemeProvider'; |
32 | import { exposeToReduxDevtools, hotReloadServices } from './devTools'; | 32 | import { exposeToReduxDevtools, hotReloadServices } from './devTools'; |
33 | import { createAndConnectRendererStore } from './stores/RendererStore'; | 33 | import { createAndConnectRendererStore } from './stores/RendererStore'; |
34 | import { getLogger } from './utils/log'; | ||
34 | 35 | ||
35 | const isDevelopment = import.meta.env.MODE === 'development'; | 36 | const isDevelopment = import.meta.env.MODE === 'development'; |
36 | 37 | ||
38 | const log = getLogger('index'); | ||
39 | |||
37 | if (isDevelopment) { | 40 | if (isDevelopment) { |
38 | hotReloadServices(); | 41 | hotReloadServices(); |
39 | document.title = `[dev] ${document.title}`; | 42 | document.title = `[dev] ${document.title}`; |
@@ -42,7 +45,9 @@ if (isDevelopment) { | |||
42 | const store = createAndConnectRendererStore(window.sophieRenderer); | 45 | const store = createAndConnectRendererStore(window.sophieRenderer); |
43 | 46 | ||
44 | if (isDevelopment) { | 47 | if (isDevelopment) { |
45 | exposeToReduxDevtools(store); | 48 | exposeToReduxDevtools(store).catch((err) => { |
49 | log.error('Cannot initialize redux devtools', err); | ||
50 | }); | ||
46 | } | 51 | } |
47 | 52 | ||
48 | function Root(): JSX.Element { | 53 | function Root(): JSX.Element { |
diff --git a/packages/renderer/src/stores/RendererEnv.ts b/packages/renderer/src/stores/RendererEnv.ts index d687738..f0a5a51 100644 --- a/packages/renderer/src/stores/RendererEnv.ts +++ b/packages/renderer/src/stores/RendererEnv.ts | |||
@@ -18,10 +18,10 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { getEnv as getAnyEnv, IAnyStateTreeNode } from 'mobx-state-tree'; | ||
22 | import type { Action } from '@sophie/shared'; | 21 | import type { Action } from '@sophie/shared'; |
22 | import { getEnv as getAnyEnv, IAnyStateTreeNode } from 'mobx-state-tree'; | ||
23 | 23 | ||
24 | export interface RendererEnv { | 24 | export default interface RendererEnv { |
25 | dispatchMainAction(action: Action): void; | 25 | dispatchMainAction(action: Action): void; |
26 | } | 26 | } |
27 | 27 | ||
diff --git a/packages/renderer/src/stores/RendererStore.ts b/packages/renderer/src/stores/RendererStore.ts index 037b212..e684759 100644 --- a/packages/renderer/src/stores/RendererStore.ts +++ b/packages/renderer/src/stores/RendererStore.ts | |||
@@ -19,19 +19,24 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { | 21 | import { |
22 | applySnapshot, | ||
23 | applyPatch, | ||
24 | Instance, | ||
25 | types, | ||
26 | } from 'mobx-state-tree'; | ||
27 | import { | ||
28 | BrowserViewBounds, | 22 | BrowserViewBounds, |
29 | sharedStore, | 23 | sharedStore, |
30 | SophieRenderer, | 24 | SophieRenderer, |
31 | ThemeSource, | 25 | ThemeSource, |
32 | } from '@sophie/shared'; | 26 | } from '@sophie/shared'; |
27 | import { | ||
28 | applySnapshot, | ||
29 | applyPatch, | ||
30 | Instance, | ||
31 | types, | ||
32 | } from 'mobx-state-tree'; | ||
33 | |||
34 | import { getLogger } from '../utils/log'; | ||
35 | |||
36 | import type RendererEnv from './RendererEnv'; | ||
37 | import { getEnv } from './RendererEnv'; | ||
33 | 38 | ||
34 | import { getEnv, RendererEnv } from './RendererEnv'; | 39 | const log = getLogger('RendererStore'); |
35 | 40 | ||
36 | export const rendererStore = types.model('RendererStore', { | 41 | export const rendererStore = types.model('RendererStore', { |
37 | shared: types.optional(sharedStore, {}), | 42 | shared: types.optional(sharedStore, {}), |
@@ -81,7 +86,7 @@ export function createAndConnectRendererStore(ipc: SophieRenderer): RendererStor | |||
81 | applyPatch(store.shared, patch); | 86 | applyPatch(store.shared, patch); |
82 | }, | 87 | }, |
83 | }).catch((err) => { | 88 | }).catch((err) => { |
84 | console.error('Failed to connect to shared store', err); | 89 | log.error('Failed to connect to shared store', err); |
85 | }); | 90 | }); |
86 | 91 | ||
87 | return store; | 92 | return store; |
diff --git a/packages/renderer/src/utils/log.ts b/packages/renderer/src/utils/log.ts new file mode 100644 index 0000000..c17fc2a --- /dev/null +++ b/packages/renderer/src/utils/log.ts | |||
@@ -0,0 +1,50 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com> | ||
3 | * | ||
4 | * This file is part of Sophie. | ||
5 | * | ||
6 | * Sophie is free software: you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU Affero General Public License as | ||
8 | * published by the Free Software Foundation, version 3. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU Affero General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Affero General Public License | ||
16 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
17 | * | ||
18 | * SPDX-License-Identifier: AGPL-3.0-only | ||
19 | */ | ||
20 | |||
21 | import loglevel, { Logger } from 'loglevel'; | ||
22 | import prefix from 'loglevel-plugin-prefix'; | ||
23 | |||
24 | if (import.meta.env?.DEV) { | ||
25 | loglevel.setLevel('debug'); | ||
26 | } else { | ||
27 | // No devtools in production, so there's not point to log anything. | ||
28 | loglevel.disableAll(); | ||
29 | } | ||
30 | |||
31 | prefix.reg(loglevel); | ||
32 | prefix.apply(loglevel, { | ||
33 | format(level, name, timestamp) { | ||
34 | const timeStr = timestamp.toString(); | ||
35 | const nameStr = typeof name === 'undefined' ? '' : ` ${name}`; | ||
36 | return `[${timeStr}] ${level}${nameStr}:`; | ||
37 | }, | ||
38 | }); | ||
39 | |||
40 | export function getLogger(loggerName: string): Logger { | ||
41 | return loglevel.getLogger(loggerName); | ||
42 | } | ||
43 | |||
44 | export function silenceLogger(): void { | ||
45 | loglevel.disableAll(); | ||
46 | const loggers = loglevel.getLoggers(); | ||
47 | Object.keys(loggers).forEach((loggerName) => { | ||
48 | loggers[loggerName].disableAll(); | ||
49 | }); | ||
50 | } | ||
diff --git a/packages/renderer/tsconfig.json b/packages/renderer/tsconfig.json index 8746462..14c3e0c 100644 --- a/packages/renderer/tsconfig.json +++ b/packages/renderer/tsconfig.json | |||
@@ -1,5 +1,5 @@ | |||
1 | { | 1 | { |
2 | "extends": "../../tsconfig.json", | 2 | "extends": "../../config/tsconfig.base.json", |
3 | "compilerOptions": { | 3 | "compilerOptions": { |
4 | "noEmit": true, | 4 | "noEmit": true, |
5 | "jsx": "react", | 5 | "jsx": "react", |
@@ -14,12 +14,14 @@ | |||
14 | }, | 14 | }, |
15 | "references": [ | 15 | "references": [ |
16 | { | 16 | { |
17 | "path": "../shared" | 17 | "path": "../shared/tsconfig.build.json" |
18 | } | 18 | } |
19 | ], | 19 | ], |
20 | "include": [ | 20 | "include": [ |
21 | "src/**/*.ts", | 21 | "src/**/*.ts", |
22 | "src/**/*.tsx", | 22 | "src/**/*.tsx", |
23 | "types/**/*.d.ts" | 23 | "types/**/*.d.ts", |
24 | ".eslintrc.cjs", | ||
25 | "vite.config.js" | ||
24 | ] | 26 | ] |
25 | } | 27 | } |
diff --git a/packages/renderer/vite.config.js b/packages/renderer/vite.config.js index bcd1975..6440ead 100644 --- a/packages/renderer/vite.config.js +++ b/packages/renderer/vite.config.js | |||
@@ -3,10 +3,11 @@ | |||
3 | 3 | ||
4 | import { builtinModules } from 'module'; | 4 | import { builtinModules } from 'module'; |
5 | import { join } from 'path'; | 5 | import { join } from 'path'; |
6 | |||
6 | import react from '@vitejs/plugin-react'; | 7 | import react from '@vitejs/plugin-react'; |
7 | 8 | ||
8 | import { banner, chrome } from '../../config/buildConstants.js'; | 9 | import { banner, chrome } from '../../config/buildConstants.js'; |
9 | import { fileURLToDirname } from '../../config/utils.js'; | 10 | import fileURLToDirname from '../../config/fileURLToDirname.js'; |
10 | 11 | ||
11 | const thisDir = fileURLToDirname(import.meta.url); | 12 | const thisDir = fileURLToDirname(import.meta.url); |
12 | 13 | ||
@@ -45,7 +46,7 @@ export default { | |||
45 | preserveSymlinks: true, | 46 | preserveSymlinks: true, |
46 | }, | 47 | }, |
47 | optimizeDeps: { | 48 | optimizeDeps: { |
48 | link: [ | 49 | exclude: [ |
49 | '@sophie/shared', | 50 | '@sophie/shared', |
50 | ], | 51 | ], |
51 | }, | 52 | }, |
diff --git a/packages/service-inject/.eslintrc.cjs b/packages/service-inject/.eslintrc.cjs new file mode 100644 index 0000000..6ae3faf --- /dev/null +++ b/packages/service-inject/.eslintrc.cjs | |||
@@ -0,0 +1,6 @@ | |||
1 | module.exports = { | ||
2 | env: { | ||
3 | node: false, | ||
4 | browser: true, | ||
5 | }, | ||
6 | }; | ||
diff --git a/packages/service-inject/esbuild.config.js b/packages/service-inject/esbuild.config.js index 2169c8e..d0b04bb 100644 --- a/packages/service-inject/esbuild.config.js +++ b/packages/service-inject/esbuild.config.js | |||
@@ -1,8 +1,8 @@ | |||
1 | import { chrome } from '../../config/buildConstants.js'; | 1 | import { chrome } from '../../config/buildConstants.js'; |
2 | import { getConfig } from '../../config/esbuildConfig.js'; | 2 | import fileURLToDirname from '../../config/fileURLToDirname.js'; |
3 | import { fileURLToDirname } from '../../config/utils.js'; | 3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; |
4 | 4 | ||
5 | export default getConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileURLToDirname(import.meta.url), |
7 | entryPoints: [ | 7 | entryPoints: [ |
8 | 'src/index.ts', | 8 | 'src/index.ts', |
diff --git a/packages/service-inject/package.json b/packages/service-inject/package.json index 7c496fd..c045500 100644 --- a/packages/service-inject/package.json +++ b/packages/service-inject/package.json | |||
@@ -6,13 +6,9 @@ | |||
6 | "type": "module", | 6 | "type": "module", |
7 | "types": "dist-types/index.d.ts", | 7 | "types": "dist-types/index.d.ts", |
8 | "scripts": { | 8 | "scripts": { |
9 | "typecheck": "tsc" | 9 | "typecheck:workspace": "yarn g:typecheck" |
10 | }, | 10 | }, |
11 | "dependencies": { | 11 | "dependencies": { |
12 | "@sophie/service-shared": "workspace:*" | 12 | "@sophie/service-shared": "workspace:*" |
13 | }, | ||
14 | "devDependencies": { | ||
15 | "rimraf": "^3.0.2", | ||
16 | "typescript": "^4.5.4" | ||
17 | } | 13 | } |
18 | } | 14 | } |
diff --git a/packages/service-inject/tsconfig.json b/packages/service-inject/tsconfig.json index 638690b..cc61d63 100644 --- a/packages/service-inject/tsconfig.json +++ b/packages/service-inject/tsconfig.json | |||
@@ -1,5 +1,5 @@ | |||
1 | { | 1 | { |
2 | "extends": "../../tsconfig.json", | 2 | "extends": "../../config/tsconfig.base.json", |
3 | "compilerOptions": { | 3 | "compilerOptions": { |
4 | "noEmit": true, | 4 | "noEmit": true, |
5 | "lib": [ | 5 | "lib": [ |
@@ -10,10 +10,11 @@ | |||
10 | }, | 10 | }, |
11 | "references": [ | 11 | "references": [ |
12 | { | 12 | { |
13 | "path": "../service-shared" | 13 | "path": "../service-shared/tsconfig.build.json" |
14 | } | 14 | } |
15 | ], | 15 | ], |
16 | "include": [ | 16 | "include": [ |
17 | "src/**/*.ts" | 17 | "src/**/*.ts", |
18 | "esbuild.config.js" | ||
18 | ] | 19 | ] |
19 | } | 20 | } |
diff --git a/packages/service-preload/.eslintrc.cjs b/packages/service-preload/.eslintrc.cjs new file mode 100644 index 0000000..02fab21 --- /dev/null +++ b/packages/service-preload/.eslintrc.cjs | |||
@@ -0,0 +1,6 @@ | |||
1 | module.exports = { | ||
2 | env: { | ||
3 | node: true, | ||
4 | browser: true, | ||
5 | }, | ||
6 | }; | ||
diff --git a/packages/service-preload/esbuild.config.js b/packages/service-preload/esbuild.config.js index b73a071..66f5e84 100644 --- a/packages/service-preload/esbuild.config.js +++ b/packages/service-preload/esbuild.config.js | |||
@@ -1,8 +1,8 @@ | |||
1 | import { chrome } from '../../config/buildConstants.js'; | 1 | import { chrome } from '../../config/buildConstants.js'; |
2 | import { getConfig } from '../../config/esbuildConfig.js'; | 2 | import fileURLToDirname from '../../config/fileURLToDirname.js'; |
3 | import { fileURLToDirname } from '../../config/utils.js'; | 3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; |
4 | 4 | ||
5 | export default getConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileURLToDirname(import.meta.url), |
7 | entryPoints: [ | 7 | entryPoints: [ |
8 | 'src/index.ts', | 8 | 'src/index.ts', |
diff --git a/packages/service-preload/package.json b/packages/service-preload/package.json index 26215a3..14717f8 100644 --- a/packages/service-preload/package.json +++ b/packages/service-preload/package.json | |||
@@ -5,14 +5,12 @@ | |||
5 | "type": "module", | 5 | "type": "module", |
6 | "types": "dist-types/index.d.ts", | 6 | "types": "dist-types/index.d.ts", |
7 | "scripts": { | 7 | "scripts": { |
8 | "typecheck": "tsc" | 8 | "typecheck:workspace": "yarn g:typecheck" |
9 | }, | 9 | }, |
10 | "dependencies": { | 10 | "dependencies": { |
11 | "@sophie/service-shared": "workspace:*", | 11 | "@sophie/service-shared": "workspace:*", |
12 | "electron": "16.0.6" | 12 | "electron": "16.0.6", |
13 | }, | 13 | "loglevel": "^1.8.0", |
14 | "devDependencies": { | 14 | "loglevel-plugin-prefix": "^0.8.4" |
15 | "rimraf": "^3.0.2", | ||
16 | "typescript": "^4.5.4" | ||
17 | } | 15 | } |
18 | } | 16 | } |
diff --git a/packages/service-preload/src/index.ts b/packages/service-preload/src/index.ts index d1ea13c..2bbfefd 100644 --- a/packages/service-preload/src/index.ts +++ b/packages/service-preload/src/index.ts | |||
@@ -18,8 +18,12 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { ipcRenderer, webFrame } from 'electron'; | ||
22 | import { ServiceToMainIpcMessage, webSource } from '@sophie/service-shared'; | 21 | import { ServiceToMainIpcMessage, webSource } from '@sophie/service-shared'; |
22 | import { ipcRenderer, webFrame } from 'electron'; | ||
23 | |||
24 | import { getLogger } from './utils/log'; | ||
25 | |||
26 | const log = getLogger('index'); | ||
23 | 27 | ||
24 | if (webFrame.parent === null) { | 28 | if (webFrame.parent === null) { |
25 | // Inject CSS to simulate `browserView.setBackgroundColor`. | 29 | // Inject CSS to simulate `browserView.setBackgroundColor`. |
@@ -49,14 +53,14 @@ if (webFrame.parent === null) { | |||
49 | * @see https://www.electronjs.org/docs/latest/api/web-contents#contentsexecutejavascriptinisolatedworldworldid-scripts-usergesture | 53 | * @see https://www.electronjs.org/docs/latest/api/web-contents#contentsexecutejavascriptinisolatedworldworldid-scripts-usergesture |
50 | */ | 54 | */ |
51 | async function fetchAndExecuteInjectScript(): Promise<void> { | 55 | async function fetchAndExecuteInjectScript(): Promise<void> { |
52 | const apiExposedResponse = await ipcRenderer.invoke( | 56 | const apiExposedResponse: unknown = await ipcRenderer.invoke( |
53 | ServiceToMainIpcMessage.ApiExposedInMainWorld, | 57 | ServiceToMainIpcMessage.ApiExposedInMainWorld, |
54 | ); | 58 | ); |
55 | const injectSource = webSource.parse(apiExposedResponse); | 59 | const injectSource = webSource.parse(apiExposedResponse); |
56 | // Isolated world 0 is the main world. | 60 | // Isolated world 0 is the main world. |
57 | return webFrame.executeJavaScriptInIsolatedWorld(0, [injectSource]); | 61 | await webFrame.executeJavaScriptInIsolatedWorld(0, [injectSource]); |
58 | } | 62 | } |
59 | 63 | ||
60 | fetchAndExecuteInjectScript().catch((err) => { | 64 | fetchAndExecuteInjectScript().catch((err) => { |
61 | console.log('Failed to fetch inject source:', err); | 65 | log.error('Failed to fetch inject source:', err); |
62 | }); | 66 | }); |
diff --git a/packages/service-preload/src/utils/log.ts b/packages/service-preload/src/utils/log.ts new file mode 100644 index 0000000..0c35319 --- /dev/null +++ b/packages/service-preload/src/utils/log.ts | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com> | ||
3 | * | ||
4 | * This file is part of Sophie. | ||
5 | * | ||
6 | * Sophie is free software: you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU Affero General Public License as | ||
8 | * published by the Free Software Foundation, version 3. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU Affero General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Affero General Public License | ||
16 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
17 | * | ||
18 | * SPDX-License-Identifier: AGPL-3.0-only | ||
19 | */ | ||
20 | |||
21 | import loglevel, { Logger } from 'loglevel'; | ||
22 | import prefix from 'loglevel-plugin-prefix'; | ||
23 | |||
24 | if (import.meta.env?.DEV) { | ||
25 | loglevel.setLevel('debug'); | ||
26 | } else { | ||
27 | loglevel.setLevel('info'); | ||
28 | } | ||
29 | |||
30 | prefix.reg(loglevel); | ||
31 | prefix.apply(loglevel, { | ||
32 | format(level, name, timestamp) { | ||
33 | const timeStr = timestamp.toString(); | ||
34 | const nameStr = typeof name === 'undefined' ? '' : ` ${name}`; | ||
35 | return `[${timeStr}] ${level}${nameStr}:`; | ||
36 | }, | ||
37 | }); | ||
38 | |||
39 | export function getLogger(loggerName: string): Logger { | ||
40 | return loglevel.getLogger(loggerName); | ||
41 | } | ||
42 | |||
43 | export function silenceLogger(): void { | ||
44 | loglevel.disableAll(); | ||
45 | const loggers = loglevel.getLoggers(); | ||
46 | Object.keys(loggers).forEach((loggerName) => { | ||
47 | loggers[loggerName].disableAll(); | ||
48 | }); | ||
49 | } | ||
diff --git a/packages/service-preload/tsconfig.json b/packages/service-preload/tsconfig.json index 638690b..0372dde 100644 --- a/packages/service-preload/tsconfig.json +++ b/packages/service-preload/tsconfig.json | |||
@@ -1,5 +1,5 @@ | |||
1 | { | 1 | { |
2 | "extends": "../../tsconfig.json", | 2 | "extends": "../../config/tsconfig.base.json", |
3 | "compilerOptions": { | 3 | "compilerOptions": { |
4 | "noEmit": true, | 4 | "noEmit": true, |
5 | "lib": [ | 5 | "lib": [ |
@@ -10,10 +10,12 @@ | |||
10 | }, | 10 | }, |
11 | "references": [ | 11 | "references": [ |
12 | { | 12 | { |
13 | "path": "../service-shared" | 13 | "path": "../service-shared/tsconfig.build.json" |
14 | } | 14 | } |
15 | ], | 15 | ], |
16 | "include": [ | 16 | "include": [ |
17 | "src/**/*.ts" | 17 | "src/**/*.ts", |
18 | "types/**/*.ts", | ||
19 | "esbuild.config.js" | ||
18 | ] | 20 | ] |
19 | } | 21 | } |
diff --git a/packages/service-preload/types/importMeta.d.ts b/packages/service-preload/types/importMeta.d.ts new file mode 100644 index 0000000..9b73170 --- /dev/null +++ b/packages/service-preload/types/importMeta.d.ts | |||
@@ -0,0 +1,7 @@ | |||
1 | interface ImportMeta { | ||
2 | env: { | ||
3 | DEV: boolean; | ||
4 | MODE: string; | ||
5 | PROD: boolean; | ||
6 | } | ||
7 | } | ||
diff --git a/packages/service-shared/.eslintrc.cjs b/packages/service-shared/.eslintrc.cjs new file mode 100644 index 0000000..71d6ec4 --- /dev/null +++ b/packages/service-shared/.eslintrc.cjs | |||
@@ -0,0 +1,7 @@ | |||
1 | module.exports = { | ||
2 | env: { | ||
3 | // We must run in both node and browser, so we can't depend on either of them. | ||
4 | node: false, | ||
5 | browser: false, | ||
6 | }, | ||
7 | }; | ||
diff --git a/packages/service-shared/esbuild.config.js b/packages/service-shared/esbuild.config.js index 08941a4..ccee72c 100644 --- a/packages/service-shared/esbuild.config.js +++ b/packages/service-shared/esbuild.config.js | |||
@@ -1,8 +1,8 @@ | |||
1 | import { chrome, node } from '../../config/buildConstants.js'; | 1 | import { chrome, node } from '../../config/buildConstants.js'; |
2 | import { getConfig } from '../../config/esbuildConfig.js'; | 2 | import fileURLToDirname from '../../config/fileURLToDirname.js'; |
3 | import { fileURLToDirname } from '../../config/utils.js'; | 3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; |
4 | 4 | ||
5 | export default getConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileURLToDirname(import.meta.url), |
7 | entryPoints: [ | 7 | entryPoints: [ |
8 | 'src/index.ts', | 8 | 'src/index.ts', |
diff --git a/packages/service-shared/package.json b/packages/service-shared/package.json index 9d75fc8..5338c8c 100644 --- a/packages/service-shared/package.json +++ b/packages/service-shared/package.json | |||
@@ -7,13 +7,10 @@ | |||
7 | "exports": "./dist/index.mjs", | 7 | "exports": "./dist/index.mjs", |
8 | "types": "dist/index.d.ts", | 8 | "types": "dist/index.d.ts", |
9 | "scripts": { | 9 | "scripts": { |
10 | "typecheck": "tsc" | 10 | "typecheck:workspace": "yarn g:typecheck", |
11 | "types": "yarn g:types" | ||
11 | }, | 12 | }, |
12 | "dependencies": { | 13 | "dependencies": { |
13 | "zod": "^3.11.6" | 14 | "zod": "^3.11.6" |
14 | }, | ||
15 | "devDependencies": { | ||
16 | "rimraf": "^3.0.2", | ||
17 | "typescript": "^4.5.4" | ||
18 | } | 15 | } |
19 | } | 16 | } |
diff --git a/packages/service-shared/src/index.ts b/packages/service-shared/src/index.ts index 564ebe8..e111347 100644 --- a/packages/service-shared/src/index.ts +++ b/packages/service-shared/src/index.ts | |||
@@ -18,7 +18,7 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | export { ServiceToMainIpcMessage } from './ipc'; | 21 | export { MainToServiceIpcMessage, ServiceToMainIpcMessage } from './ipc'; |
22 | 22 | ||
23 | export type { | 23 | export type { |
24 | UnreadCount, | 24 | UnreadCount, |
diff --git a/packages/service-shared/src/ipc.ts b/packages/service-shared/src/ipc.ts index 4f991c5..c0dab11 100644 --- a/packages/service-shared/src/ipc.ts +++ b/packages/service-shared/src/ipc.ts | |||
@@ -18,6 +18,9 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | export enum MainToServiceIpcMessage { | ||
22 | } | ||
23 | |||
21 | export enum ServiceToMainIpcMessage { | 24 | export enum ServiceToMainIpcMessage { |
22 | ApiExposedInMainWorld = 'sophie-service-to-main:api-exposed-in-main-world', | 25 | ApiExposedInMainWorld = 'sophie-service-to-main:api-exposed-in-main-world', |
23 | SetUnreadCount = 'sophie-service-to-main:set-unread-count', | 26 | SetUnreadCount = 'sophie-service-to-main:set-unread-count', |
diff --git a/packages/service-shared/tsconfig.build.json b/packages/service-shared/tsconfig.build.json new file mode 100644 index 0000000..9a0c835 --- /dev/null +++ b/packages/service-shared/tsconfig.build.json | |||
@@ -0,0 +1,12 @@ | |||
1 | { | ||
2 | "extends": "../../config/tsconfig.base.json", | ||
3 | "compilerOptions": { | ||
4 | "composite": true, | ||
5 | "declarationDir": "dist", | ||
6 | "emitDeclarationOnly": true, | ||
7 | "rootDir": "src" | ||
8 | }, | ||
9 | "include": [ | ||
10 | "src/**/*.ts" | ||
11 | ] | ||
12 | } | ||
diff --git a/packages/service-shared/tsconfig.json b/packages/service-shared/tsconfig.json index ff5a29b..79889d2 100644 --- a/packages/service-shared/tsconfig.json +++ b/packages/service-shared/tsconfig.json | |||
@@ -1,12 +1,14 @@ | |||
1 | { | 1 | { |
2 | "extends": "../../tsconfig.json", | 2 | "extends": "./tsconfig.build.json", |
3 | "compilerOptions": { | 3 | "compilerOptions": { |
4 | "composite": true, | 4 | "composite": false, |
5 | "declarationDir": "dist", | 5 | "emitDeclarationOnly": false, |
6 | "emitDeclarationOnly": true, | 6 | "declarationDir": null, |
7 | "rootDir": "src" | 7 | "noEmit": true, |
8 | "rootDir": null | ||
8 | }, | 9 | }, |
9 | "include": [ | 10 | "include": [ |
10 | "src/**/*.ts" | 11 | "src/**/*.ts", |
12 | "esbuild.config.js" | ||
11 | ] | 13 | ] |
12 | } | 14 | } |
diff --git a/packages/shared/.eslintrc.cjs b/packages/shared/.eslintrc.cjs new file mode 100644 index 0000000..71d6ec4 --- /dev/null +++ b/packages/shared/.eslintrc.cjs | |||
@@ -0,0 +1,7 @@ | |||
1 | module.exports = { | ||
2 | env: { | ||
3 | // We must run in both node and browser, so we can't depend on either of them. | ||
4 | node: false, | ||
5 | browser: false, | ||
6 | }, | ||
7 | }; | ||
diff --git a/packages/shared/esbuild.config.js b/packages/shared/esbuild.config.js index 66d6658..78249ab 100644 --- a/packages/shared/esbuild.config.js +++ b/packages/shared/esbuild.config.js | |||
@@ -1,8 +1,8 @@ | |||
1 | import { chrome, node } from '../../config/buildConstants.js'; | 1 | import { chrome, node } from '../../config/buildConstants.js'; |
2 | import { getConfig } from '../../config/esbuildConfig.js'; | 2 | import fileURLToDirname from '../../config/fileURLToDirname.js'; |
3 | import { fileURLToDirname } from '../../config/utils.js'; | 3 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; |
4 | 4 | ||
5 | export default getConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileURLToDirname(import.meta.url), |
7 | entryPoints: [ | 7 | entryPoints: [ |
8 | 'src/index.ts', | 8 | 'src/index.ts', |
diff --git a/packages/shared/package.json b/packages/shared/package.json index 0c06643..d77261d 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json | |||
@@ -7,15 +7,12 @@ | |||
7 | "exports": "./dist/index.mjs", | 7 | "exports": "./dist/index.mjs", |
8 | "types": "dist/index.d.ts", | 8 | "types": "dist/index.d.ts", |
9 | "scripts": { | 9 | "scripts": { |
10 | "typecheck": "tsc" | 10 | "typecheck:workspace": "yarn g:typecheck", |
11 | "types": "yarn g:types" | ||
11 | }, | 12 | }, |
12 | "dependencies": { | 13 | "dependencies": { |
13 | "mobx": "^6.3.12", | 14 | "mobx": "^6.3.12", |
14 | "mobx-state-tree": "^5.1.0", | 15 | "mobx-state-tree": "^5.1.0", |
15 | "zod": "^3.11.6" | 16 | "zod": "^3.11.6" |
16 | }, | ||
17 | "devDependencies": { | ||
18 | "rimraf": "^3.0.2", | ||
19 | "typescript": "^4.5.4" | ||
20 | } | 17 | } |
21 | } | 18 | } |
diff --git a/packages/shared/src/contextBridge/SophieRenderer.ts b/packages/shared/src/contextBridge/SophieRenderer.ts index fc43b6e..9858aa9 100644 --- a/packages/shared/src/contextBridge/SophieRenderer.ts +++ b/packages/shared/src/contextBridge/SophieRenderer.ts | |||
@@ -18,12 +18,11 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { SharedStoreListener } from '../stores/SharedStore'; | ||
22 | |||
23 | import { Action } from '../schemas'; | 21 | import { Action } from '../schemas'; |
22 | import { SharedStoreListener } from '../stores/SharedStore'; | ||
24 | 23 | ||
25 | export interface SophieRenderer { | 24 | export interface SophieRenderer { |
26 | onSharedStoreChange(listener: SharedStoreListener): Promise<void>; | 25 | onSharedStoreChange(this: void, listener: SharedStoreListener): Promise<void>; |
27 | 26 | ||
28 | dispatchAction(action: Action): void; | 27 | dispatchAction(this: void, action: Action): void; |
29 | } | 28 | } |
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 2f7146c..9828ec4 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts | |||
@@ -20,10 +20,7 @@ | |||
20 | 20 | ||
21 | export type { SophieRenderer } from './contextBridge/SophieRenderer'; | 21 | export type { SophieRenderer } from './contextBridge/SophieRenderer'; |
22 | 22 | ||
23 | export { | 23 | export { MainToRendererIpcMessage, RendererToMainIpcMessage } from './ipc'; |
24 | MainToRendererIpcMessage, | ||
25 | RendererToMainIpcMessage, | ||
26 | } from './ipc'; | ||
27 | 24 | ||
28 | export type { | 25 | export type { |
29 | Action, | 26 | Action, |
diff --git a/packages/shared/tsconfig.build.json b/packages/shared/tsconfig.build.json new file mode 100644 index 0000000..9a0c835 --- /dev/null +++ b/packages/shared/tsconfig.build.json | |||
@@ -0,0 +1,12 @@ | |||
1 | { | ||
2 | "extends": "../../config/tsconfig.base.json", | ||
3 | "compilerOptions": { | ||
4 | "composite": true, | ||
5 | "declarationDir": "dist", | ||
6 | "emitDeclarationOnly": true, | ||
7 | "rootDir": "src" | ||
8 | }, | ||
9 | "include": [ | ||
10 | "src/**/*.ts" | ||
11 | ] | ||
12 | } | ||
diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json index ff5a29b..79889d2 100644 --- a/packages/shared/tsconfig.json +++ b/packages/shared/tsconfig.json | |||
@@ -1,12 +1,14 @@ | |||
1 | { | 1 | { |
2 | "extends": "../../tsconfig.json", | 2 | "extends": "./tsconfig.build.json", |
3 | "compilerOptions": { | 3 | "compilerOptions": { |
4 | "composite": true, | 4 | "composite": false, |
5 | "declarationDir": "dist", | 5 | "emitDeclarationOnly": false, |
6 | "emitDeclarationOnly": true, | 6 | "declarationDir": null, |
7 | "rootDir": "src" | 7 | "noEmit": true, |
8 | "rootDir": null | ||
8 | }, | 9 | }, |
9 | "include": [ | 10 | "include": [ |
10 | "src/**/*.ts" | 11 | "src/**/*.ts", |
12 | "esbuild.config.js" | ||
11 | ] | 13 | ] |
12 | } | 14 | } |