aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-01-08 21:36:43 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-01-09 19:53:03 +0100
commitd07e7b834831230b53860d0919a68edc2d36193d (patch)
treea1f2a021563ddff54f33341c475fc6c6eb787388
parentNew configurations based on review comments (WIP) (diff)
downloadsophie-d07e7b834831230b53860d0919a68edc2d36193d.tar.gz
sophie-d07e7b834831230b53860d0919a68edc2d36193d.tar.zst
sophie-d07e7b834831230b53860d0919a68edc2d36193d.zip
build: Eslint fixes for multi-module project
Signed-off-by: Kristóf Marussy <kristof@marussy.com>
-rw-r--r--.eslintignore1
-rw-r--r--.eslintrc.cjs126
-rw-r--r--.eslintrc.json191
-rw-r--r--.gitlab-ci.yml35
-rw-r--r--.yarnrc.yml23
-rw-r--r--README.md8
-rw-r--r--config/buildConstants.js4
-rw-r--r--config/fileURLToDirname.js (renamed from config/utils.js)2
-rw-r--r--config/getEsbuildConfig.js (renamed from config/esbuildConfig.js)2
-rw-r--r--config/jest.config.base.js4
-rw-r--r--config/jestEsbuildTransform.js23
-rw-r--r--config/jestEsbuildTransformer.js28
-rw-r--r--config/tsconfig.base.json19
-rw-r--r--jest.config.js3
-rw-r--r--package.json16
-rw-r--r--packages/main/.eslintrc.cjs6
-rw-r--r--packages/main/.eslintrc.json6
-rw-r--r--packages/main/esbuild.config.js8
-rw-r--r--packages/main/package.json2
-rw-r--r--packages/main/src/controllers/__tests__/initConfig.spec.ts (renamed from packages/main/src/controllers/__tests__/config.spec.ts)18
-rw-r--r--packages/main/src/controllers/__tests__/initNativeTheme.spec.ts (renamed from packages/main/src/controllers/__tests__/nativeTheme.spec.ts)4
-rw-r--r--packages/main/src/controllers/initConfig.ts (renamed from packages/main/src/controllers/config.ts)12
-rw-r--r--packages/main/src/controllers/initNativeTheme.ts (renamed from packages/main/src/controllers/nativeTheme.ts)8
-rw-r--r--packages/main/src/devTools.ts7
-rw-r--r--packages/main/src/index.ts109
-rw-r--r--packages/main/src/init.ts (renamed from packages/main/src/compositionRoot.ts)10
-rw-r--r--packages/main/src/services/ConfigPersistenceService.ts6
-rw-r--r--packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts20
-rw-r--r--packages/main/src/stores/Config.ts2
-rw-r--r--packages/main/src/stores/MainStore.ts2
-rw-r--r--packages/main/src/stores/SharedStore.ts2
-rw-r--r--packages/main/src/utils/Disposer.ts (renamed from packages/main/src/utils/disposer.ts)4
-rw-r--r--packages/main/src/utils/log.ts (renamed from packages/main/src/utils/logging.ts)10
-rw-r--r--packages/main/tsconfig.json10
-rw-r--r--packages/preload/.eslintrc.cjs6
-rw-r--r--packages/preload/esbuild.config.js6
-rw-r--r--packages/preload/jest.config.js2
-rw-r--r--packages/preload/package.json6
-rw-r--r--packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts (renamed from packages/preload/src/contextBridge/__tests__/SophieRendererImpl.spec.ts)26
-rw-r--r--packages/preload/src/contextBridge/createSophieRenderer.ts (renamed from packages/preload/src/contextBridge/SophieRendererImpl.ts)42
-rw-r--r--packages/preload/src/index.ts2
-rw-r--r--packages/preload/tsconfig.json8
-rw-r--r--packages/renderer/.eslinrc.cjs11
-rw-r--r--packages/renderer/.eslintrc.json5
-rw-r--r--packages/renderer/package.json10
-rw-r--r--packages/renderer/src/components/App.tsx6
-rw-r--r--packages/renderer/src/components/BrowserViewPlaceholder.tsx14
-rw-r--r--packages/renderer/src/components/Sidebar.tsx4
-rw-r--r--packages/renderer/src/components/StoreProvider.tsx2
-rw-r--r--packages/renderer/src/components/ThemeProvider.tsx8
-rw-r--r--packages/renderer/src/components/ToggleDarkModeButton.tsx9
-rw-r--r--packages/renderer/src/devTools.ts21
-rw-r--r--packages/renderer/src/index.tsx13
-rw-r--r--packages/renderer/src/stores/RendererEnv.ts4
-rw-r--r--packages/renderer/src/stores/RendererStore.ts21
-rw-r--r--packages/renderer/src/utils/log.ts50
-rw-r--r--packages/renderer/tsconfig.json8
-rw-r--r--packages/renderer/vite.config.js5
-rw-r--r--packages/service-inject/.eslintrc.cjs6
-rw-r--r--packages/service-inject/esbuild.config.js6
-rw-r--r--packages/service-inject/package.json6
-rw-r--r--packages/service-inject/tsconfig.json7
-rw-r--r--packages/service-preload/.eslintrc.cjs6
-rw-r--r--packages/service-preload/esbuild.config.js6
-rw-r--r--packages/service-preload/package.json10
-rw-r--r--packages/service-preload/src/index.ts12
-rw-r--r--packages/service-preload/src/utils/log.ts49
-rw-r--r--packages/service-preload/tsconfig.json8
-rw-r--r--packages/service-preload/types/importMeta.d.ts7
-rw-r--r--packages/service-shared/.eslintrc.cjs7
-rw-r--r--packages/service-shared/esbuild.config.js6
-rw-r--r--packages/service-shared/package.json7
-rw-r--r--packages/service-shared/src/index.ts2
-rw-r--r--packages/service-shared/src/ipc.ts3
-rw-r--r--packages/service-shared/tsconfig.build.json12
-rw-r--r--packages/service-shared/tsconfig.json14
-rw-r--r--packages/shared/.eslintrc.cjs7
-rw-r--r--packages/shared/esbuild.config.js6
-rw-r--r--packages/shared/package.json7
-rw-r--r--packages/shared/src/contextBridge/SophieRenderer.ts7
-rw-r--r--packages/shared/src/index.ts5
-rw-r--r--packages/shared/tsconfig.build.json12
-rw-r--r--packages/shared/tsconfig.json14
-rw-r--r--scripts/build.js6
-rw-r--r--scripts/update-electron-vendors.js14
-rw-r--r--scripts/watch.js52
-rw-r--r--tsconfig.json27
-rw-r--r--yarn.lock202
88 files changed, 956 insertions, 597 deletions
diff --git a/.eslintignore b/.eslintignore
index cf31955..01b8bcb 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,4 +1,5 @@
1# Logs 1# Logs
2.log/
2logs 3logs
3*.log 4*.log
4 5
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
new file mode 100644
index 0000000..92af738
--- /dev/null
+++ b/.eslintrc.cjs
@@ -0,0 +1,126 @@
1const project = [
2 './tsconfig.json',
3 './packages/*/tsconfig.json',
4];
5
6module.exports = {
7 root: true,
8 plugins: [
9 '@typescript-eslint',
10 ],
11 extends: [
12 'airbnb-base',
13 'airbnb-typescript/base',
14 'plugin:@typescript-eslint/recommended',
15 'plugin:@typescript-eslint/recommended-requiring-type-checking',
16 ],
17 env: {
18 es6: true,
19 node: true,
20 browser: false,
21 jest: true,
22 },
23 parser: '@typescript-eslint/parser',
24 parserOptions: {
25 sourceType: 'module',
26 project,
27 allowAutomaticSingleRunInference: true,
28 tsconfigRootDir: __dirname,
29 warnOnUnsupportedTypeScriptVersion: false,
30 },
31 settings: {
32 'import/parsers': {
33 '@typescript-eslint/parser': ['.ts', '.tsx'],
34 },
35 'import/resolver': {
36 typescript: {
37 alwaysTryTypes: true,
38 project,
39 },
40 },
41 },
42 rules: {
43 'import/no-unresolved': 'error',
44 'import/order': [
45 'error',
46 {
47 alphabetize: {
48 order: 'asc',
49 },
50 'newlines-between': 'always',
51 },
52 ],
53 },
54 overrides: [
55 {
56 files: [
57 '**/stores/**/*.ts',
58 ],
59 rules: {
60 // In a mobx-state-tree action, we assign to the properties of `self` to update the store.
61 'no-param-reassign': 'off',
62 // mobx-state-tree uses empty interfaces to speed up typechecking.
63 '@typescript-eslint/no-empty-interface': 'off',
64 },
65 },
66 {
67 files: [
68 '**/__tests__/*.{ts,tsx}',
69 '**/*.{spec,test}.{ts,tsx}',
70 ],
71 rules: {
72 // If a non-null assertion fails in a test, the test will also fail anyways.
73 '@typescript-eslint/no-non-null-assertion': 'off',
74 // Jest mocks use unbound method references.
75 '@typescript-eslint/unbound-method': 'off',
76 },
77 },
78 {
79 files: [
80 '**/*.js',
81 ],
82 rules: {
83 // ESM requires extensions for imports.
84 'import/extensions': [
85 'error',
86 'ignorePackages',
87 ],
88 },
89 },
90 {
91 files: [
92 '.electron-builder.config.cjs',
93 'config/**/*.{cjs,js}',
94 'jest.config.js',
95 'scripts/**/*.js',
96 'packages/*/*.config.js',
97 ],
98 env: {
99 // Config files are never run in a browser (even in frontend projects).
100 browser: false,
101 node: true,
102 jest: false,
103 },
104 rules: {
105 // Config files and build scripts are allowed to use devDependencies.
106 'import/no-extraneous-dependencies': [
107 'error',
108 {
109 devDependencies: true,
110 },
111 ],
112 // Allow reporting progress to the console from scripts.
113 'no-console': 'off',
114 },
115 },
116 {
117 files: [
118 'packages/*/*.config.js',
119 ],
120 rules: {
121 // Allow relative imports of config files from the root package.
122 'import/no-relative-packages': 'off',
123 },
124 },
125 ],
126};
diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index 82f5d58..0000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,191 +0,0 @@
1{
2 "root": true,
3 "env": {
4 "node": true,
5 "browser": true,
6 "es2021": true
7 },
8 "extends": [
9 "eslint-config-airbnb-typescript",
10 "plugin:import/recommended",
11 "plugin:import/typescript"
12 ],
13 "parser": "@typescript-eslint/parser",
14 "parserOptions": {
15 "ecmaFeatures": {
16 "jsx": true
17 },
18 "ecmaVersion": 2021,
19 "project": "./tsconfig.json"
20 },
21 "plugins": [
22 "react",
23 "@typescript-eslint"
24 ],
25 "rules": {
26 "indent": [
27 2,
28 2,
29 {
30 "SwitchCase": 1
31 }
32 ],
33 "quotes": [
34 "error",
35 "single"
36 ],
37 "linebreak-style": [
38 "error",
39 "unix"
40 ],
41 "semi": [
42 "error",
43 "always"
44 ],
45 "import/extensions": [
46 "error",
47 "ignorePackages",
48 {
49 "js": "always",
50 "jsx": "always",
51 "json": "never",
52 "ts": "never",
53 "tsx": "never"
54 }
55 ],
56 "import/no-unresolved": [
57 "error",
58 {
59 "caseSensitive": false
60 }
61 ],
62 "import/no-extraneous-dependencies": [
63 "error",
64 {
65 // "devDependencies": true,
66 // "optionalDependencies": true,
67 // "peerDependencies": true,
68 "bundledDependencies": true
69 }
70 ],
71 // Best practices
72 "block-scoped-var": 1,
73 "complexity": [
74 1,
75 4
76 ],
77 "consistent-return": 1,
78 "curly": 1,
79 "default-case": 1,
80 "dot-location": [
81 1,
82 "property"
83 ],
84 "dot-notation": 1,
85 "eqeqeq": 2,
86 "guard-for-in": 1,
87 "no-alert": 2,
88 "no-caller": 2,
89 "no-case-declarations": 2,
90 "no-console": 0,
91 "no-div-regex": 1,
92 "no-else-return": 0,
93 "no-empty": 0,
94 "no-empty-pattern": 2,
95 "no-eq-null": 2,
96 "no-eval": 2,
97 "no-extend-native": 1,
98 "no-extra-bind": 1,
99 "no-fallthrough": 1,
100 "no-floating-decimal": 1,
101 "no-implicit-coercion": 1,
102 "no-implied-eval": 1,
103 "no-invalid-this": 2,
104 "no-iterator": 2,
105 "no-labels": 1,
106 "no-lone-blocks": 1,
107 "no-loop-func": 2,
108 "no-magic-numbers": [
109 "error",
110 {
111 "ignore": [
112 -1,
113 0,
114 1,
115 2,
116 100,
117 200,
118 422,
119 3600000,
120 1453449120000,
121 1453445460000
122 ]
123 }
124 ],
125 "no-multi-spaces": 1,
126 "no-multi-str": 1,
127 "no-native-reassign": 1,
128 "no-new-func": 2,
129 "no-new-wrappers": 2,
130 "no-new": 1,
131 "no-octal-escape": 1,
132 "no-octal": 1,
133 "no-param-reassign": 1,
134 "no-process-env": 2,
135 "no-proto": 2,
136 "no-redeclare": 1,
137 "no-return-assign": 2,
138 "no-script-url": 2,
139 "no-self-compare": 1,
140 "no-sequences": 1,
141 "no-throw-literal": 2,
142 "no-unused-expressions": [
143 1,
144 {
145 "allowTernary": true
146 }
147 ],
148 "no-useless-call": 2,
149 "no-useless-concat": 1,
150 "no-void": 2,
151 "no-warning-comments": 0,
152 "no-with": 2,
153 "radix": 1,
154 "vars-on-top": 0,
155 "wrap-iife": 2,
156 "yoda": 0,
157 "strict": 1,
158 // Variables
159 "init-declarations": 0,
160 "no-catch-shadow": 2,
161 "no-delete-var": 2,
162 "no-label-var": 2,
163 "no-shadow-restricted-names": 2,
164 "no-shadow": 2,
165 "no-undef-init": 1,
166 "no-undef": 2,
167 "no-undefined": 0,
168 "no-unused-vars": 2,
169 "no-use-before-define": 2
170 },
171 "globals": {
172 "__dirname": false
173 },
174 "overrides": [
175 {
176 "files": [
177 "**/__tests__/*"
178 ],
179 "globals": {
180 "after": false,
181 "afterEach": false,
182 "beforeAll": false,
183 "beforeEach": false,
184 "describe": false,
185 "Electron": false,
186 "expect": false,
187 "it": false
188 }
189 }
190 ]
191}
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index bf9461e..c522036 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,35 +2,38 @@ image: node:16.13.1
2 2
3cache: 3cache:
4 paths: 4 paths:
5 - node_modules/ 5 - .yarn/cache/
6 - .yarn/cache
7 6
8stages: 7stages:
8 - code-quality
9 - test 9 - test
10 - lint
11 - build 10 - build
12 11
13Run tests: 12default:
14 stage: test
15 before_script: 13 before_script:
16 - yarn install 14 - yarn install --immutable
15
16lint:
17 stage: code-quality
17 script: 18 script:
18 - yarn test 19 - yarn lint --format gitlab
20 artifacts:
21 reports:
22 codequality: gl-codequality.json
19 23
20Run linter and static analyzer: 24typecheck:
21 stage: lint 25 stage: code-quality
22 before_script:
23 - yarn install
24 script: 26 script:
25 - yarn dlx @yarnpkg/doctor
26 - yarn typecheck 27 - yarn typecheck
27 - yarn run lint 28
29test:
30 stage: test
31 script:
32 - yarn test
28 33
29# TODO: GitlabCI free runners are only for linux - need to investigate for macos and windows artifacts 34# TODO: GitlabCI free runners are only for linux - need to investigate for macos and windows artifacts
30Build the app: 35build:
31 stage: build 36 stage: build
32 before_script:
33 - yarn install
34 script: 37 script:
35 - yarn compile 38 - yarn compile
36 # TODO: Need to publish the built distributable file 39 # TODO: Need to publish the built distributable file
diff --git a/.yarnrc.yml b/.yarnrc.yml
index f83ee48..e8caff1 100644
--- a/.yarnrc.yml
+++ b/.yarnrc.yml
@@ -1,13 +1,6 @@
1plugins: 1enableTelemetry: 0
2 - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
3 spec: "@yarnpkg/plugin-interactive-tools"
4 - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
5 spec: "@yarnpkg/plugin-workspace-tools"
6 - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
7 spec: "@yarnpkg/plugin-typescript"
8 2
9logFilters: 3logFilters:
10 # Discard incompatible package architecture warnings.
11 - code: YN0076 4 - code: YN0076
12 level: discard 5 level: discard
13 6
@@ -15,4 +8,18 @@ nmMode: hardlinks-local
15 8
16nodeLinker: node-modules 9nodeLinker: node-modules
17 10
11packageExtensions:
12 eslint-config-airbnb-typescript@*:
13 peerDependencies:
14 eslint: "*"
15 eslint-plugin-import: "*"
16
17plugins:
18 - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
19 spec: "@yarnpkg/plugin-interactive-tools"
20 - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
21 spec: "@yarnpkg/plugin-workspace-tools"
22 - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
23 spec: "@yarnpkg/plugin-typescript"
24
18yarnPath: .yarn/releases/yarn-3.1.1.cjs 25yarnPath: .yarn/releases/yarn-3.1.1.cjs
diff --git a/README.md b/README.md
index 6a2eba0..28f9ec5 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,12 @@ To start working, install all dependencies with
25yarn install 25yarn install
26``` 26```
27 27
28If TypeScript complains about missing type definitions, run
29
30```sh
31yarn types
32```
33
28To start a development instance of Sophie, which will reload on source changes, run 34To start a development instance of Sophie, which will reload on source changes, run
29 35
30``` sh 36``` sh
@@ -49,7 +55,7 @@ To build the application in release mode, run
49yarn compile 55yarn compile
50``` 56```
51 57
52If TypeScript complains about missing type definitions, or you want to typecheck the project, run 58To typecheck the project, run
53 59
54```sh 60```sh
55yarn typecheck 61yarn typecheck
diff --git a/config/buildConstants.js b/config/buildConstants.js
index 627c895..9083d78 100644
--- a/config/buildConstants.js
+++ b/config/buildConstants.js
@@ -1,7 +1,7 @@
1import { readFileSync } from 'fs'; 1import { readFileSync } from 'fs';
2import { join } from 'path'; 2import { join } from 'path';
3 3
4import { fileURLToDirname } from './utils.js'; 4import fileURLToDirname from './fileURLToDirname.js';
5 5
6const thisDir = fileURLToDirname(import.meta.url); 6const thisDir = fileURLToDirname(import.meta.url);
7 7
@@ -9,6 +9,8 @@ const thisDir = fileURLToDirname(import.meta.url);
9// so we have to use the synchronous filesystem API. 9// so we have to use the synchronous filesystem API.
10const electronVendorsJson = readFileSync(join(thisDir, '../.electron-vendors.cache.json'), 'utf8'); 10const electronVendorsJson = readFileSync(join(thisDir, '../.electron-vendors.cache.json'), 'utf8');
11 11
12/** @type {{ chrome: number; node: number; }} */
13// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
12const { chrome: chromeVersion, node: nodeVersion } = JSON.parse(electronVendorsJson); 14const { chrome: chromeVersion, node: nodeVersion } = JSON.parse(electronVendorsJson);
13 15
14/** @type {string} */ 16/** @type {string} */
diff --git a/config/utils.js b/config/fileURLToDirname.js
index d3e13d9..70654cb 100644
--- a/config/utils.js
+++ b/config/fileURLToDirname.js
@@ -5,6 +5,6 @@ import { fileURLToPath } from 'url';
5 * @param {string} url 5 * @param {string} url
6 * @returns {string} 6 * @returns {string}
7 */ 7 */
8export function fileURLToDirname(url) { 8export default function fileURLToDirname(url) {
9 return dirname(fileURLToPath(url)); 9 return dirname(fileURLToPath(url));
10} 10}
diff --git a/config/esbuildConfig.js b/config/getEsbuildConfig.js
index 93419fb..b338d68 100644
--- a/config/esbuildConfig.js
+++ b/config/getEsbuildConfig.js
@@ -15,7 +15,7 @@ const modeString = JSON.stringify(mode);
15 * @param {Record<string, unknown>} [extraMetaEnvVars] 15 * @param {Record<string, unknown>} [extraMetaEnvVars]
16 * @returns {import('esbuild').BuildOptions} 16 * @returns {import('esbuild').BuildOptions}
17 */ 17 */
18export function getConfig(config, extraMetaEnvVars) { 18export default function getEsbuildConfig(config, extraMetaEnvVars) {
19 return { 19 return {
20 logLevel: 'info', 20 logLevel: 'info',
21 bundle: true, 21 bundle: true,
diff --git a/config/jest.config.base.js b/config/jest.config.base.js
index 2ca021b..463e498 100644
--- a/config/jest.config.base.js
+++ b/config/jest.config.base.js
@@ -1,13 +1,13 @@
1import { join } from 'path'; 1import { join } from 'path';
2 2
3import { fileURLToDirname } from './utils.js'; 3import fileURLToDirname from './fileURLToDirname.js';
4 4
5const thisDir = fileURLToDirname(import.meta.url); 5const thisDir = fileURLToDirname(import.meta.url);
6 6
7/** @type {import('@jest/types').Config.InitialOptions} */ 7/** @type {import('@jest/types').Config.InitialOptions} */
8export default { 8export default {
9 transform: { 9 transform: {
10 '\\.tsx?$': join(thisDir, 'jestEsbuildTransform.js'), 10 '\\.tsx?$': join(thisDir, 'jestEsbuildTransformer.js'),
11 }, 11 },
12 extensionsToTreatAsEsm: [ 12 extensionsToTreatAsEsm: [
13 '.ts', 13 '.ts',
diff --git a/config/jestEsbuildTransform.js b/config/jestEsbuildTransform.js
deleted file mode 100644
index 4bf49e6..0000000
--- a/config/jestEsbuildTransform.js
+++ /dev/null
@@ -1,23 +0,0 @@
1import { transform } from 'esbuild';
2
3import { node } from './buildConstants.js';
4
5/** @type {import('@jest/transform').AsyncTransformer<undefined>} */
6const transformer = {
7 canInstrument: false,
8 async processAsync(source, filePath) {
9 const { code, map } = await transform(source, {
10 loader: filePath.endsWith('tsx') ? 'tsx' : 'ts',
11 sourcefile: filePath,
12 format: 'esm',
13 target: node,
14 sourcemap: true,
15 });
16 return {
17 code,
18 map,
19 };
20 },
21};
22
23export default transformer;
diff --git a/config/jestEsbuildTransformer.js b/config/jestEsbuildTransformer.js
new file mode 100644
index 0000000..b6f2fc3
--- /dev/null
+++ b/config/jestEsbuildTransformer.js
@@ -0,0 +1,28 @@
1import { transform } from 'esbuild';
2
3import { node } from './buildConstants.js';
4
5/**
6 * @param {string} source
7 * @param {import('@jest/types').Config.Path} filePath
8 * @return {Promise<import('@jest/transform').TransformedSource>}
9 */
10async function processAsync(source, filePath) {
11 const { code, map } = await transform(source, {
12 loader: filePath.endsWith('tsx') ? 'tsx' : 'ts',
13 sourcefile: filePath,
14 format: 'esm',
15 target: node,
16 sourcemap: true,
17 });
18 return {
19 code,
20 map,
21 };
22}
23
24/** @type {import('@jest/transform').AsyncTransformer<void>} */
25export default {
26 canInstrument: false,
27 processAsync,
28};
diff --git a/config/tsconfig.base.json b/config/tsconfig.base.json
new file mode 100644
index 0000000..255f334
--- /dev/null
+++ b/config/tsconfig.base.json
@@ -0,0 +1,19 @@
1{
2 "compilerOptions": {
3 "module": "esnext",
4 "target": "esnext",
5 "moduleResolution": "node",
6 "esModuleInterop": true,
7 "allowSyntheticDefaultImports": true,
8 "strict": true,
9 "noImplicitOverride": true,
10 "noImplicitReturns": true,
11 "exactOptionalPropertyTypes": true,
12 "isolatedModules": true,
13 "skipLibCheck": true,
14 "checkJs": true,
15 "lib": [
16 "esnext"
17 ]
18 }
19}
diff --git a/jest.config.js b/jest.config.js
index 74e18b7..c631fdd 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -1,8 +1,9 @@
1/** @type {import('ts-jest').InitialOptionsTsJest} */ 1/** @type {import('@jest/types').Config.InitialOptions} */
2export default { 2export default {
3 projects: [ 3 projects: [
4 '<rootDir>/packages/*', 4 '<rootDir>/packages/*',
5 ], 5 ],
6 /** @type {'babel' | 'v8'} */
6 coverageProvider: 'v8', 7 coverageProvider: 'v8',
7 collectCoverageFrom: [ 8 collectCoverageFrom: [
8 'src/**/*.{ts,tsx}', 9 'src/**/*.{ts,tsx}',
diff --git a/package.json b/package.json
index 99cc7a2..f632a8c 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
19 "type": "module", 19 "type": "module",
20 "main": "packages/main/dist/index.cjs", 20 "main": "packages/main/dist/index.cjs",
21 "scripts": { 21 "scripts": {
22 "clean": "rimraf dist packages/*/dist packages/*/tsconfig.tsbuildinfo .vite", 22 "clean": "rimraf coverage dist packages/*/dist packages/*/*.tsbuildinfo .vite",
23 "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js", 23 "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
24 "build": "node scripts/build.js", 24 "build": "node scripts/build.js",
25 "precompile": "cross-env MODE=production yarn run build", 25 "precompile": "cross-env MODE=production yarn run build",
@@ -27,8 +27,12 @@
27 "compile:electron-builder": "electron-builder build --config .electron-builder.config.cjs --dir", 27 "compile:electron-builder": "electron-builder build --config .electron-builder.config.cjs --dir",
28 "watch": "node scripts/watch.js", 28 "watch": "node scripts/watch.js",
29 "watch:test": "yarn test --watch", 29 "watch:test": "yarn test --watch",
30 "typecheck": "yarn workspaces foreach -vpt run typecheck", 30 "lint": "yarn types && eslint .",
31 "lint": "eslint \"{config,packages,scripts}/**/*.{js,jsx,ts,tsx}\" --quiet", 31 "typecheck": "yarn types && yarn workspaces foreach -vp run typecheck:workspace",
32 "typecheck:workspace": "yarn g:typecheck",
33 "g:typecheck": "cd $INIT_CWD && tsc",
34 "types": "yarn workspaces foreach -vpt run types",
35 "g:types": "cd $INIT_CWD && tsc -b tsconfig.build.json",
32 "update-electron-vendors": "node scripts/update-electron-vendors.js", 36 "update-electron-vendors": "node scripts/update-electron-vendors.js",
33 "main": "yarn workspace @sophie/main", 37 "main": "yarn workspace @sophie/main",
34 "preload": "yarn workspace @sophie/preload", 38 "preload": "yarn workspace @sophie/preload",
@@ -53,9 +57,15 @@
53 "electron-builder": "^22.14.12", 57 "electron-builder": "^22.14.12",
54 "esbuild": "^0.14.11", 58 "esbuild": "^0.14.11",
55 "eslint": "^8.6.0", 59 "eslint": "^8.6.0",
60 "eslint-config-airbnb": "^19.0.4",
61 "eslint-config-airbnb-base": "^15.0.0",
56 "eslint-config-airbnb-typescript": "^16.1.0", 62 "eslint-config-airbnb-typescript": "^16.1.0",
63 "eslint-formatter-gitlab": "^3.0.0",
64 "eslint-import-resolver-typescript": "^2.5.0",
57 "eslint-plugin-import": "^2.25.4", 65 "eslint-plugin-import": "^2.25.4",
66 "eslint-plugin-jsx-a11y": "^6.5.1",
58 "eslint-plugin-react": "^7.28.0", 67 "eslint-plugin-react": "^7.28.0",
68 "eslint-plugin-react-hooks": "^4.3.0",
59 "git-repo-info": "^2.1.1", 69 "git-repo-info": "^2.1.1",
60 "jest": "^27.4.7", 70 "jest": "^27.4.7",
61 "rimraf": "^3.0.2", 71 "rimraf": "^3.0.2",
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 @@
1module.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 */
2import getRepoInfo from 'git-repo-info'; 1import getRepoInfo from 'git-repo-info';
2
3import { node } from '../../config/buildConstants.js'; 3import { node } from '../../config/buildConstants.js';
4import { getConfig } from '../../config/esbuildConfig.js'; 4import fileURLToDirname from '../../config/fileURLToDirname.js';
5import { fileURLToDirname } from '../../config/utils.js'; 5import getEsbuildConfig from '../../config/getEsbuildConfig.js';
6 6
7const externalPackages = ['electron']; 7const externalPackages = ['electron'];
8 8
@@ -12,7 +12,7 @@ if (process.env.MODE !== 'development') {
12 12
13const gitInfo = getRepoInfo(); 13const gitInfo = getRepoInfo();
14 14
15export default getConfig({ 15export 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';
22import { mocked } from 'jest-mock'; 22import { mocked } from 'jest-mock';
23import ms from 'ms'; 23import ms from 'ms';
24 24
25import { initConfig } from '../config'; 25import type ConfigPersistenceService from '../../services/ConfigPersistenceService';
26import type { ConfigPersistenceService } from '../../services/ConfigPersistenceService';
27import { Config, config as configModel } from '../../stores/Config'; 26import { Config, config as configModel } from '../../stores/Config';
28import { Disposer } from '../../utils/disposer'; 27import type Disposer from '../../utils/Disposer';
29import { silenceLogger } from '../../utils/logging'; 28import { silenceLogger } from '../../utils/log';
29import initConfig from '../initConfig';
30 30
31let config: Config; 31let config: Config;
32let persistenceService: ConfigPersistenceService = { 32const 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};
37let lessThanThrottleMs = ms('0.1s'); 37const lessThanThrottleMs = ms('0.1s');
38let throttleMs = ms('1s'); 38const throttleMs = ms('1s');
39 39
40beforeAll(() => { 40beforeAll(() => {
41 jest.useFakeTimers(); 41 jest.useFakeTimers();
@@ -108,7 +108,7 @@ describe('when initializing', () => {
108 108
109describe('when it has loaded the config', () => { 109describe('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';
22import { mocked } from 'jest-mock'; 22import { mocked } from 'jest-mock';
23 23
24import { createMainStore, MainStore } from '../../stores/MainStore'; 24import { createMainStore, MainStore } from '../../stores/MainStore';
25import { Disposer } from '../../utils/disposer'; 25import type Disposer from '../../utils/Disposer';
26 26
27let shouldUseDarkColors = false; 27let shouldUseDarkColors = false;
28 28
@@ -38,7 +38,7 @@ jest.unstable_mockModule('electron', () => ({
38})); 38}));
39 39
40const { nativeTheme } = await import('electron'); 40const { nativeTheme } = await import('electron');
41const { initNativeTheme } = await import('../nativeTheme'); 41const { default: initNativeTheme } = await import('../initNativeTheme');
42 42
43let store: MainStore; 43let store: MainStore;
44let disposeSut: Disposer; 44let 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
21import { debounce } from 'lodash-es'; 21import { debounce } from 'lodash-es';
22import ms from 'ms';
23import { applySnapshot, getSnapshot, onSnapshot } from 'mobx-state-tree'; 22import { applySnapshot, getSnapshot, onSnapshot } from 'mobx-state-tree';
23import ms from 'ms';
24 24
25import type { ConfigPersistenceService } from '../services/ConfigPersistenceService.js'; 25import type ConfigPersistenceService from '../services/ConfigPersistenceService';
26import type { Config, ConfigSnapshotOut } from '../stores/Config.js'; 26import type { Config, ConfigSnapshotOut } from '../stores/Config';
27import { Disposer } from '../utils/disposer'; 27import type Disposer from '../utils/Disposer';
28import { getLogger } from '../utils/logging'; 28import { getLogger } from '../utils/log';
29 29
30const DEFAULT_CONFIG_DEBOUNCE_TIME = ms('1s'); 30const DEFAULT_CONFIG_DEBOUNCE_TIME = ms('1s');
31 31
32const log = getLogger('config'); 32const log = getLogger('config');
33 33
34export async function initConfig( 34export 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 @@
21import { nativeTheme } from 'electron'; 21import { nativeTheme } from 'electron';
22import { autorun } from 'mobx'; 22import { autorun } from 'mobx';
23 23
24import type { MainStore } from '../stores/MainStore.js'; 24import type { MainStore } from '../stores/MainStore';
25import { Disposer } from '../utils/disposer'; 25import type Disposer from '../utils/Disposer';
26import { getLogger } from '../utils/logging'; 26import { getLogger } from '../utils/log';
27 27
28const log = getLogger('nativeTheme'); 28const log = getLogger('nativeTheme');
29 29
30export function initNativeTheme(store: MainStore): Disposer { 30export 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
22import {
23 app,
24 BrowserView,
25 BrowserWindow,
26 ipcMain,
27} from 'electron';
28import { arch } from 'os'; 22import { arch } from 'os';
29import osName from 'os-name';
30import { ensureDirSync, readFile, readFileSync } from 'fs-extra';
31import { autorun } from 'mobx';
32import { getSnapshot, onPatch } from 'mobx-state-tree';
33import { join } from 'path'; 23import { join } from 'path';
24import { URL } from 'url';
25
34import { 26import {
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';
44import { URL } from 'url'; 36import {
37 app,
38 BrowserView,
39 BrowserWindow,
40 ipcMain,
41} from 'electron';
42import { ensureDirSync, readFile, readFileSync } from 'fs-extra';
43import { autorun } from 'mobx';
44import { getSnapshot, onPatch } from 'mobx-state-tree';
45import osName from 'os-name';
45 46
46import { init } from './compositionRoot';
47import { 47import {
48 DEVMODE_ALLOWED_URL_PREFIXES, 48 DEVMODE_ALLOWED_URL_PREFIXES,
49 installDevToolsExtensions, 49 installDevToolsExtensions,
50 openDevToolsWhenReady, 50 openDevToolsWhenReady,
51} from './devTools'; 51} from './devTools';
52import init from './init';
52import { createMainStore } from './stores/MainStore'; 53import { createMainStore } from './stores/MainStore';
54import { getLogger } from './utils/log';
53 55
54const isDevelopment = import.meta.env.MODE === 'development'; 56const isDevelopment = import.meta.env.MODE === 'development';
55 57
58const log = getLogger('index');
59
56// Always enable sandboxing. 60// Always enable sandboxing.
57app.enableSandbox(); 61app.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
112let serviceInjectRelativePath = '../../service-inject/dist/index.js'; 116const serviceInjectRelativePath = '../../service-inject/dist/index.js';
113let serviceInjectPath = getResourcePath(serviceInjectRelativePath); 117const serviceInjectPath = getResourcePath(serviceInjectRelativePath);
114let serviceInject: WebSource = { 118const 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();
122init(store).then((disposeCompositionRoot) => { 126init(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
128const rendererBaseUrl = getResourceUrl('../renderer/'); 132const 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
21import { app } from 'electron'; 21import { app } from 'electron';
22 22
23import { initConfig } from './controllers/config'; 23import initConfig from './controllers/initConfig';
24import { initNativeTheme } from './controllers/nativeTheme'; 24import initNativeTheme from './controllers/initNativeTheme';
25import { ConfigPersistenceServiceImpl } from './services/impl/ConfigPersistenceServiceImpl'; 25import ConfigPersistenceServiceImpl from './services/impl/ConfigPersistenceServiceImpl';
26import { MainStore } from './stores/MainStore'; 26import { MainStore } from './stores/MainStore';
27import { Disposer } from './utils/disposer'; 27import type Disposer from './utils/Disposer';
28 28
29export async function init(store: MainStore): Promise<Disposer> { 29export 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
21import type { ConfigSnapshotOut } from '../stores/Config'; 21import type { ConfigSnapshotOut } from '../stores/Config';
22import { Disposer } from '../utils/disposer'; 22import type Disposer from '../utils/Disposer';
23 23
24export type ReadConfigResult = { found: true; data: unknown; } | { found: false; }; 24export type ReadConfigResult = { found: true; data: unknown; } | { found: false; };
25 25
26export interface ConfigPersistenceService { 26export 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 */
20import { watch } from 'fs'; 20import { watch } from 'fs';
21import { readFile, stat, writeFile } from 'fs/promises'; 21import { readFile, stat, writeFile } from 'fs/promises';
22import JSON5 from 'json5';
23import { throttle } from 'lodash-es';
24import { join } from 'path'; 22import { join } from 'path';
25 23
26import type { ConfigPersistenceService, ReadConfigResult } from '../ConfigPersistenceService.js'; 24import JSON5 from 'json5';
27import type { ConfigSnapshotOut } from '../../stores/Config.js'; 25import throttle from 'lodash-es/throttle';
28import { Disposer } from '../../utils/disposer'; 26
29import { getLogger } from '../../utils/logging'; 27import type { ConfigSnapshotOut } from '../../stores/Config';
28import type Disposer from '../../utils/Disposer';
29import { getLogger } from '../../utils/log';
30import type ConfigPersistenceService from '../ConfigPersistenceService';
31import type { ReadConfigResult } from '../ConfigPersistenceService';
30 32
31const log = getLogger('configPersistence'); 33const log = getLogger('configPersistence');
32 34
33export class ConfigPersistenceServiceImpl implements ConfigPersistenceService { 35export 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
21import { Instance } from 'mobx-state-tree';
22import { 21import {
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';
27import { Instance } from 'mobx-state-tree';
28 28
29export const config = originalConfig.actions((self) => ({ 29export 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
21import { applySnapshot, Instance, types } from 'mobx-state-tree';
22import { BrowserViewBounds } from '@sophie/shared'; 21import { BrowserViewBounds } from '@sophie/shared';
22import { applySnapshot, Instance, types } from 'mobx-state-tree';
23 23
24import type { Config } from './Config.js'; 24import type { Config } from './Config.js';
25import { sharedStore } from './SharedStore'; 25import { 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
21import { Instance, types } from 'mobx-state-tree';
22import { sharedStore as originalSharedStore } from '@sophie/shared'; 21import { sharedStore as originalSharedStore } from '@sophie/shared';
22import { Instance, types } from 'mobx-state-tree';
23 23
24import { config } from './Config'; 24import { 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
21import { IDisposer } from 'mobx-state-tree'; 21import { IDisposer } from 'mobx-state-tree';
22 22
23export type Disposer = IDisposer; 23type Disposer = IDisposer;
24
25export 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);
45prefix.apply(loglevel, { 45prefix.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 {
56export function silenceLogger(): void { 60export 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 @@
1module.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 @@
1import { chrome } from '../../config/buildConstants.js'; 1import { chrome } from '../../config/buildConstants.js';
2import { getConfig } from '../../config/esbuildConfig.js'; 2import fileURLToDirname from '../../config/fileURLToDirname.js';
3import { fileURLToDirname } from '../../config/utils.js'; 3import getEsbuildConfig from '../../config/getEsbuildConfig.js';
4 4
5export default getConfig({ 5export 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 @@
1import rootConfig from '../../config/jest.config.base.js'; 1import rootConfig from '../../config/jest.config.base.js';
2 2
3/** @type {import('ts-jest').InitialOptionsTsJest} */ 3/** @type {import('@jest/types').Config.InitialOptions} */
4export default { 4export 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
21import { jest } from '@jest/globals'; 21import { jest } from '@jest/globals';
22import { mocked } from 'jest-mock';
23import log from 'loglevel';
24import type { IJsonPatch } from 'mobx-state-tree';
25import { 22import {
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';
29import { mocked } from 'jest-mock';
30import log from 'loglevel';
31import type { IJsonPatch } from 'mobx-state-tree';
32 32
33jest.unstable_mockModule('electron', () => ({ 33jest.unstable_mockModule('electron', () => ({
34 ipcRenderer: { 34 ipcRenderer: {
@@ -40,7 +40,7 @@ jest.unstable_mockModule('electron', () => ({
40 40
41const { ipcRenderer } = await import('electron'); 41const { ipcRenderer } = await import('electron');
42 42
43const { createSophieRenderer } = await import('../SophieRendererImpl'); 43const { default: createSophieRenderer } = await import('../createSophieRenderer');
44 44
45const event: Electron.IpcRendererEvent = null as unknown as Electron.IpcRendererEvent; 45const event: Electron.IpcRendererEvent = null as unknown as Electron.IpcRendererEvent;
46 46
@@ -81,10 +81,10 @@ describe('createSophieRenderer', () => {
81 }); 81 });
82}); 82});
83 83
84describe('SophieRendererImpl', () => { 84describe('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
21import { ipcRenderer } from 'electron';
22import log from 'loglevel';
23import type { IJsonPatch } from 'mobx-state-tree';
24import { 21import {
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';
30import { ipcRenderer } from 'electron';
31import log from 'loglevel';
32import type { IJsonPatch } from 'mobx-state-tree';
33 33
34class SophieRendererImpl implements SophieRenderer { 34class 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 { 76function 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
90export function createSophieRenderer(allowReplaceListener: boolean): SophieRenderer { 90export 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
21import { contextBridge } from 'electron'; 21import { contextBridge } from 'electron';
22 22
23import { createSophieRenderer } from './contextBridge/SophieRendererImpl'; 23import createSophieRenderer from './contextBridge/createSophieRenderer';
24 24
25const isDevelopment = import.meta.env.MODE === 'development'; 25const 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 @@
1module.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 @@
21import Box from '@mui/material/Box'; 21import Box from '@mui/material/Box';
22import React from 'react'; 22import React from 'react';
23 23
24import { BrowserViewPlaceholder } from './BrowserViewPlaceholder'; 24import BrowserViewPlaceholder from './BrowserViewPlaceholder';
25import { Sidebar } from './Sidebar'; 25import Sidebar from './Sidebar';
26 26
27export function App(): JSX.Element { 27export 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
21import { throttle } from 'lodash';
22import { observer } from 'mobx-react-lite';
23import Box from '@mui/material/Box'; 21import Box from '@mui/material/Box';
22import throttle from 'lodash-es/throttle';
23import { observer } from 'mobx-react-lite';
24import React, { useCallback, useRef } from 'react'; 24import React, { useCallback, useRef } from 'react';
25 25
26import { useStore } from './StoreProvider'; 26import { useStore } from './StoreProvider';
27 27
28export const BrowserViewPlaceholder = observer(function BrowserViewPlaceholder() { 28export 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 @@
21import Box from '@mui/material/Box'; 21import Box from '@mui/material/Box';
22import React from 'react'; 22import React from 'react';
23 23
24import { ToggleDarkModeButton } from './ToggleDarkModeButton'; 24import ToggleDarkModeButton from './ToggleDarkModeButton';
25 25
26export function Sidebar(): JSX.Element { 26export 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
35export function StoreProvider({ children, store }: { 35export 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
21import { observer } from 'mobx-react-lite';
22import { 21import {
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';
25import { observer } from 'mobx-react-lite';
26import React from 'react'; 26import React from 'react';
27 27
28import { useStore } from './StoreProvider'; 28import { useStore } from './StoreProvider';
29 29
30export const ThemeProvider = observer(function ThemeProvider({ children }: { 30export 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
21import { observer } from 'mobx-react-lite';
22import DarkModeIcon from '@mui/icons-material/DarkMode'; 21import DarkModeIcon from '@mui/icons-material/DarkMode';
23import LightModeIcon from '@mui/icons-material/LightMode'; 22import LightModeIcon from '@mui/icons-material/LightMode';
24import IconButton from '@mui/material/IconButton'; 23import IconButton from '@mui/material/IconButton';
24import { observer } from 'mobx-react-lite';
25import React from 'react'; 25import React from 'react';
26 26
27import { useStore } from './StoreProvider'; 27import { useStore } from './StoreProvider';
28 28
29export const ToggleDarkModeButton = observer(function ToggleDarkModeButton() { 29export 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 */
37async function exposeToReduxDevtoolsAsync(model: IAnyStateTreeNode): Promise<void> { 38export 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 */
53export 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';
26import React from 'react'; 26import React from 'react';
27import { render } from 'react-dom'; 27import { render } from 'react-dom';
28 28
29import { App } from './components/App'; 29import App from './components/App';
30import { StoreProvider } from './components/StoreProvider'; 30import StoreProvider from './components/StoreProvider';
31import { ThemeProvider } from './components/ThemeProvider'; 31import ThemeProvider from './components/ThemeProvider';
32import { exposeToReduxDevtools, hotReloadServices } from './devTools'; 32import { exposeToReduxDevtools, hotReloadServices } from './devTools';
33import { createAndConnectRendererStore } from './stores/RendererStore'; 33import { createAndConnectRendererStore } from './stores/RendererStore';
34import { getLogger } from './utils/log';
34 35
35const isDevelopment = import.meta.env.MODE === 'development'; 36const isDevelopment = import.meta.env.MODE === 'development';
36 37
38const log = getLogger('index');
39
37if (isDevelopment) { 40if (isDevelopment) {
38 hotReloadServices(); 41 hotReloadServices();
39 document.title = `[dev] ${document.title}`; 42 document.title = `[dev] ${document.title}`;
@@ -42,7 +45,9 @@ if (isDevelopment) {
42const store = createAndConnectRendererStore(window.sophieRenderer); 45const store = createAndConnectRendererStore(window.sophieRenderer);
43 46
44if (isDevelopment) { 47if (isDevelopment) {
45 exposeToReduxDevtools(store); 48 exposeToReduxDevtools(store).catch((err) => {
49 log.error('Cannot initialize redux devtools', err);
50 });
46} 51}
47 52
48function Root(): JSX.Element { 53function 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
21import { getEnv as getAnyEnv, IAnyStateTreeNode } from 'mobx-state-tree';
22import type { Action } from '@sophie/shared'; 21import type { Action } from '@sophie/shared';
22import { getEnv as getAnyEnv, IAnyStateTreeNode } from 'mobx-state-tree';
23 23
24export interface RendererEnv { 24export 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
21import { 21import {
22 applySnapshot,
23 applyPatch,
24 Instance,
25 types,
26} from 'mobx-state-tree';
27import {
28 BrowserViewBounds, 22 BrowserViewBounds,
29 sharedStore, 23 sharedStore,
30 SophieRenderer, 24 SophieRenderer,
31 ThemeSource, 25 ThemeSource,
32} from '@sophie/shared'; 26} from '@sophie/shared';
27import {
28 applySnapshot,
29 applyPatch,
30 Instance,
31 types,
32} from 'mobx-state-tree';
33
34import { getLogger } from '../utils/log';
35
36import type RendererEnv from './RendererEnv';
37import { getEnv } from './RendererEnv';
33 38
34import { getEnv, RendererEnv } from './RendererEnv'; 39const log = getLogger('RendererStore');
35 40
36export const rendererStore = types.model('RendererStore', { 41export 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
21import loglevel, { Logger } from 'loglevel';
22import prefix from 'loglevel-plugin-prefix';
23
24if (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
31prefix.reg(loglevel);
32prefix.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
40export function getLogger(loggerName: string): Logger {
41 return loglevel.getLogger(loggerName);
42}
43
44export 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
4import { builtinModules } from 'module'; 4import { builtinModules } from 'module';
5import { join } from 'path'; 5import { join } from 'path';
6
6import react from '@vitejs/plugin-react'; 7import react from '@vitejs/plugin-react';
7 8
8import { banner, chrome } from '../../config/buildConstants.js'; 9import { banner, chrome } from '../../config/buildConstants.js';
9import { fileURLToDirname } from '../../config/utils.js'; 10import fileURLToDirname from '../../config/fileURLToDirname.js';
10 11
11const thisDir = fileURLToDirname(import.meta.url); 12const 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 @@
1module.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 @@
1import { chrome } from '../../config/buildConstants.js'; 1import { chrome } from '../../config/buildConstants.js';
2import { getConfig } from '../../config/esbuildConfig.js'; 2import fileURLToDirname from '../../config/fileURLToDirname.js';
3import { fileURLToDirname } from '../../config/utils.js'; 3import getEsbuildConfig from '../../config/getEsbuildConfig.js';
4 4
5export default getConfig({ 5export 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 @@
1module.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 @@
1import { chrome } from '../../config/buildConstants.js'; 1import { chrome } from '../../config/buildConstants.js';
2import { getConfig } from '../../config/esbuildConfig.js'; 2import fileURLToDirname from '../../config/fileURLToDirname.js';
3import { fileURLToDirname } from '../../config/utils.js'; 3import getEsbuildConfig from '../../config/getEsbuildConfig.js';
4 4
5export default getConfig({ 5export 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
21import { ipcRenderer, webFrame } from 'electron';
22import { ServiceToMainIpcMessage, webSource } from '@sophie/service-shared'; 21import { ServiceToMainIpcMessage, webSource } from '@sophie/service-shared';
22import { ipcRenderer, webFrame } from 'electron';
23
24import { getLogger } from './utils/log';
25
26const log = getLogger('index');
23 27
24if (webFrame.parent === null) { 28if (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 */
51async function fetchAndExecuteInjectScript(): Promise<void> { 55async 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
60fetchAndExecuteInjectScript().catch((err) => { 64fetchAndExecuteInjectScript().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
21import loglevel, { Logger } from 'loglevel';
22import prefix from 'loglevel-plugin-prefix';
23
24if (import.meta.env?.DEV) {
25 loglevel.setLevel('debug');
26} else {
27 loglevel.setLevel('info');
28}
29
30prefix.reg(loglevel);
31prefix.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
39export function getLogger(loggerName: string): Logger {
40 return loglevel.getLogger(loggerName);
41}
42
43export 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 @@
1interface 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 @@
1module.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 @@
1import { chrome, node } from '../../config/buildConstants.js'; 1import { chrome, node } from '../../config/buildConstants.js';
2import { getConfig } from '../../config/esbuildConfig.js'; 2import fileURLToDirname from '../../config/fileURLToDirname.js';
3import { fileURLToDirname } from '../../config/utils.js'; 3import getEsbuildConfig from '../../config/getEsbuildConfig.js';
4 4
5export default getConfig({ 5export 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
21export { ServiceToMainIpcMessage } from './ipc'; 21export { MainToServiceIpcMessage, ServiceToMainIpcMessage } from './ipc';
22 22
23export type { 23export 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
21export enum MainToServiceIpcMessage {
22}
23
21export enum ServiceToMainIpcMessage { 24export 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 @@
1module.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 @@
1import { chrome, node } from '../../config/buildConstants.js'; 1import { chrome, node } from '../../config/buildConstants.js';
2import { getConfig } from '../../config/esbuildConfig.js'; 2import fileURLToDirname from '../../config/fileURLToDirname.js';
3import { fileURLToDirname } from '../../config/utils.js'; 3import getEsbuildConfig from '../../config/getEsbuildConfig.js';
4 4
5export default getConfig({ 5export 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
21import { SharedStoreListener } from '../stores/SharedStore';
22
23import { Action } from '../schemas'; 21import { Action } from '../schemas';
22import { SharedStoreListener } from '../stores/SharedStore';
24 23
25export interface SophieRenderer { 24export 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
21export type { SophieRenderer } from './contextBridge/SophieRenderer'; 21export type { SophieRenderer } from './contextBridge/SophieRenderer';
22 22
23export { 23export { MainToRendererIpcMessage, RendererToMainIpcMessage } from './ipc';
24 MainToRendererIpcMessage,
25 RendererToMainIpcMessage,
26} from './ipc';
27 24
28export type { 25export 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}
diff --git a/scripts/build.js b/scripts/build.js
index ef2b998..1236a6c 100644
--- a/scripts/build.js
+++ b/scripts/build.js
@@ -1,8 +1,9 @@
1import { build as esbuildBuild } from 'esbuild';
2import { join } from 'path'; 1import { join } from 'path';
2
3import { build as esbuildBuild } from 'esbuild';
3import { build as viteBuild } from 'vite'; 4import { build as viteBuild } from 'vite';
4 5
5import { fileURLToDirname } from '../config/utils.js'; 6import fileURLToDirname from '../config/fileURLToDirname.js';
6 7
7const thisDir = fileURLToDirname(import.meta.url); 8const thisDir = fileURLToDirname(import.meta.url);
8 9
@@ -12,6 +13,7 @@ const thisDir = fileURLToDirname(import.meta.url);
12 */ 13 */
13async function buildPackageEsbuild(packageName) { 14async function buildPackageEsbuild(packageName) {
14 /** @type {{ default: import('esbuild').BuildOptions }} */ 15 /** @type {{ default: import('esbuild').BuildOptions }} */
16 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Read untyped config file.
15 const { default: config } = await import(`../packages/${packageName}/esbuild.config.js`); 17 const { default: config } = await import(`../packages/${packageName}/esbuild.config.js`);
16 return esbuildBuild(config); 18 return esbuildBuild(config);
17} 19}
diff --git a/scripts/update-electron-vendors.js b/scripts/update-electron-vendors.js
index 70d3afc..91cdb61 100644
--- a/scripts/update-electron-vendors.js
+++ b/scripts/update-electron-vendors.js
@@ -1,9 +1,10 @@
1import { execSync } from 'child_process'; 1import { execSync } from 'child_process';
2import electronPath from 'electron';
3import { writeFile } from 'fs/promises'; 2import { writeFile } from 'fs/promises';
4import { join } from 'path'; 3import { join } from 'path';
5 4
6import { fileURLToDirname } from '../config/utils.js'; 5import electronPath from 'electron';
6
7import fileURLToDirname from '../config/fileURLToDirname.js';
7 8
8const thisDir = fileURLToDirname(import.meta.url); 9const thisDir = fileURLToDirname(import.meta.url);
9 10
@@ -15,11 +16,12 @@ const thisDir = fileURLToDirname(import.meta.url);
15 * @returns {NodeJS.ProcessVersions} 16 * @returns {NodeJS.ProcessVersions}
16 */ 17 */
17function getVendors() { 18function getVendors() {
18 const output = execSync(`${electronPath} -p "JSON.stringify(process.versions)"`, { 19 const output = execSync(`${electronPath.toString()} -p "JSON.stringify(process.versions)"`, {
19 env: { 'ELECTRON_RUN_AS_NODE': '1' }, 20 env: { ELECTRON_RUN_AS_NODE: '1' },
20 encoding: 'utf-8', 21 encoding: 'utf-8',
21 }); 22 });
22 23
24 // eslint-disable-next-line @typescript-eslint/no-unsafe-return -- Read untyped output.
23 return JSON.parse(output); 25 return JSON.parse(output);
24} 26}
25 27
@@ -39,10 +41,10 @@ function updateVendors() {
39 return Promise.all([ 41 return Promise.all([
40 writeFile( 42 writeFile(
41 join(thisDir, '../.electron-vendors.cache.json'), 43 join(thisDir, '../.electron-vendors.cache.json'),
42 JSON.stringify({ 44 `${JSON.stringify({
43 chrome: chromeMajorVersion, 45 chrome: chromeMajorVersion,
44 node: nodeMajorVersion, 46 node: nodeMajorVersion,
45 }, null, 2) + '\n', 47 }, null, 2)}\n`,
46 ), 48 ),
47 49
48 writeFile(browserslistrcPath, `Chrome ${chromeMajorVersion}\n`, 'utf8'), 50 writeFile(browserslistrcPath, `Chrome ${chromeMajorVersion}\n`, 'utf8'),
diff --git a/scripts/watch.js b/scripts/watch.js
index a61d3c8..1345a0f 100644
--- a/scripts/watch.js
+++ b/scripts/watch.js
@@ -1,11 +1,12 @@
1import { build as esbuildBuild } from 'esbuild';
2import { spawn } from 'child_process'; 1import { spawn } from 'child_process';
2import { join } from 'path';
3
3import { watch } from 'chokidar'; 4import { watch } from 'chokidar';
4import electronPath from 'electron'; 5import electronPath from 'electron';
5import { join } from 'path'; 6import { build as esbuildBuild } from 'esbuild';
6import { createServer } from 'vite'; 7import { createServer } from 'vite';
7 8
8import { fileURLToDirname } from '../config/utils.js'; 9import fileURLToDirname from '../config/fileURLToDirname.js';
9 10
10/** @type {string} */ 11/** @type {string} */
11const thisDir = fileURLToDirname(import.meta.url); 12const thisDir = fileURLToDirname(import.meta.url);
@@ -35,6 +36,7 @@ const stderrIgnorePatterns = [
35 */ 36 */
36async function setupEsbuildWatcher(packageName, extraPaths, callback) { 37async function setupEsbuildWatcher(packageName, extraPaths, callback) {
37 /** @type {{ default: import('esbuild').BuildOptions }} */ 38 /** @type {{ default: import('esbuild').BuildOptions }} */
39 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Read untyped config file.
38 const { default: config } = await import(`../packages/${packageName}/esbuild.config.js`); 40 const { default: config } = await import(`../packages/${packageName}/esbuild.config.js`);
39 config.logLevel = 'info'; 41 config.logLevel = 'info';
40 config.incremental = true; 42 config.incremental = true;
@@ -45,7 +47,7 @@ async function setupEsbuildWatcher(packageName, extraPaths, callback) {
45 ...(extraPaths || []), 47 ...(extraPaths || []),
46 ]; 48 ];
47 const watcher = watch(paths, { 49 const watcher = watch(paths, {
48 ignored: /(^|[\/\\])\.|__(tests|mocks)__|\.(spec|test)\.[jt]sx?$/, 50 ignored: /(^|[/\\])\.|__(tests|mocks)__|\.(spec|test)\.[jt]sx?$/,
49 ignoreInitial: true, 51 ignoreInitial: true,
50 persistent: true, 52 persistent: true,
51 }); 53 });
@@ -59,13 +61,26 @@ async function setupEsbuildWatcher(packageName, extraPaths, callback) {
59 callback(); 61 callback();
60 } 62 }
61 }).catch((err) => { 63 }).catch((err) => {
62 const errCount = err.errors.length; 64 if (typeof err === 'object' && 'errors' in err) {
65 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- We just checked.
66 const { errors } = err;
67 if (Array.isArray(errors)) {
68 const errCount = errors.length;
69 console.error(
70 '\ud83d\udd25',
71 errCount,
72 errCount > 1 ? 'errors' : 'error',
73 'while rebuilding package',
74 packageName,
75 );
76 return;
77 }
78 }
63 console.error( 79 console.error(
64 '\ud83d\udd25', 80 '\ud83d\udd25',
65 errCount, 81 'error while rebuilding package',
66 errCount > 1 ? 'errors' : 'error',
67 'while rebuilding package',
68 packageName, 82 packageName,
83 err,
69 ); 84 );
70 }); 85 });
71 }); 86 });
@@ -121,13 +136,14 @@ function setupServicePackageWatcher(packageName, sendEvent) {
121 */ 136 */
122function setupMainPackageWatcher(viteDevServer) { 137function setupMainPackageWatcher(viteDevServer) {
123 // Write a value to an environment variable to pass it to the main process. 138 // Write a value to an environment variable to pass it to the main process.
124 const protocol = `http${viteDevServer.config.server.https ? 's' : ''}:`; 139 const { config: { server: { port, https, host } } } = viteDevServer;
125 const host = viteDevServer.config.server.host || 'localhost'; 140 const protocol = `http${https ? 's' : ''}:`;
126 const port = viteDevServer.config.server.port; 141 const hostOrDefault = typeof host === 'string' ? host : 'localhost';
127 const path = '/'; 142 const portOrDefault = port || 3000;
128 process.env.VITE_DEV_SERVER_URL = `${protocol}//${host}:${port}${path}`; 143 process.env.VITE_DEV_SERVER_URL = `${protocol}//${hostOrDefault}:${portOrDefault}/`;
129 144
130 /** @type {import('child_process').ChildProcessByStdio<null, null, import('stream').Readable> | null} */ 145 /** @type {import('child_process').ChildProcessByStdio<null, null, import('stream').Readable>
146 | null} */
131 let spawnProcess = null; 147 let spawnProcess = null;
132 148
133 return setupEsbuildWatcher( 149 return setupEsbuildWatcher(
@@ -146,8 +162,8 @@ function setupMainPackageWatcher(viteDevServer) {
146 stdio: ['inherit', 'inherit', 'pipe'], 162 stdio: ['inherit', 'inherit', 'pipe'],
147 }); 163 });
148 164
149 spawnProcess.stderr.on('data', (data) => { 165 spawnProcess.stderr.on('data', (/** @type {Buffer} */ data) => {
150 const stderrString = data.toString('utf-8').trimRight(); 166 const stderrString = data.toString('utf-8').trimEnd();
151 if (!stderrIgnorePatterns.some((r) => r.test(stderrString))) { 167 if (!stderrIgnorePatterns.some((r) => r.test(stderrString))) {
152 console.error(stderrString); 168 console.error(stderrString);
153 } 169 }
@@ -193,7 +209,7 @@ async function setupDevEnvironment() {
193 } 209 }
194 210
195 console.log('\ud83c\udf80 Sophie is starting up'); 211 console.log('\ud83c\udf80 Sophie is starting up');
196 return setupMainPackageWatcher(viteDevServer); 212 await setupMainPackageWatcher(viteDevServer);
197} 213}
198 214
199setupDevEnvironment().catch((err) => { 215setupDevEnvironment().catch((err) => {
diff --git a/tsconfig.json b/tsconfig.json
index 255f334..627bed2 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,19 +1,14 @@
1{ 1{
2 "extends": "./config/tsconfig.base.json",
2 "compilerOptions": { 3 "compilerOptions": {
3 "module": "esnext", 4 "noEmit": true
4 "target": "esnext", 5 },
5 "moduleResolution": "node", 6 "include": [
6 "esModuleInterop": true, 7 "config/**/*.js",
7 "allowSyntheticDefaultImports": true, 8 "config/**/*.cjs",
8 "strict": true, 9 "scripts/**/*.js",
9 "noImplicitOverride": true, 10 ".electron-builder.config.cjs",
10 "noImplicitReturns": true, 11 ".eslintrc.cjs",
11 "exactOptionalPropertyTypes": true, 12 "jest.config.js"
12 "isolatedModules": true, 13 ]
13 "skipLibCheck": true,
14 "checkJs": true,
15 "lib": [
16 "esnext"
17 ]
18 }
19} 14}
diff --git a/yarn.lock b/yarn.lock
index 86fe235..55deb08 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -420,12 +420,22 @@ __metadata:
420 languageName: node 420 languageName: node
421 linkType: hard 421 linkType: hard
422 422
423"@babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.7": 423"@babel/runtime-corejs3@npm:^7.10.2":
424 version: 7.16.5 424 version: 7.16.7
425 resolution: "@babel/runtime@npm:7.16.5" 425 resolution: "@babel/runtime-corejs3@npm:7.16.7"
426 dependencies: 426 dependencies:
427 core-js-pure: ^3.19.0
427 regenerator-runtime: ^0.13.4 428 regenerator-runtime: ^0.13.4
428 checksum: b96e67280efe581c6147b4fe984dfe08a8fbea048934a092f3cbf4dcf61725f6b221cb0c879b6e6e98671f83a104c9e8cfbd24c683e5ebcc886a731aa8984ad0 429 checksum: c40cabaead64e4843a24b064cdeeabf87780bf06567146234eca94a64acb760225a9f31151eec1913c91f6f4c86afad325c5fec9262a5434e8b0a3ea905d51cf
430 languageName: node
431 linkType: hard
432
433"@babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.7":
434 version: 7.16.7
435 resolution: "@babel/runtime@npm:7.16.7"
436 dependencies:
437 regenerator-runtime: ^0.13.4
438 checksum: 47912f0aaacd1cab2e2552aaf3e6eaffbcaf2d5ac9b07a89a12ac0d42029cb92c070b0d16f825e4277c4a34677c54d8ffe85e1f7c6feb57de58f700eec67ce2f
429 languageName: node 439 languageName: node
430 linkType: hard 440 linkType: hard
431 441
@@ -1266,8 +1276,6 @@ __metadata:
1266 loglevel: ^1.8.0 1276 loglevel: ^1.8.0
1267 mobx: ^6.3.12 1277 mobx: ^6.3.12
1268 mobx-state-tree: ^5.1.0 1278 mobx-state-tree: ^5.1.0
1269 rimraf: ^3.0.2
1270 typescript: ^4.5.4
1271 languageName: unknown 1279 languageName: unknown
1272 linkType: soft 1280 linkType: soft
1273 1281
@@ -1281,11 +1289,13 @@ __metadata:
1281 "@mui/icons-material": ^5.2.5 1289 "@mui/icons-material": ^5.2.5
1282 "@mui/material": ^5.2.7 1290 "@mui/material": ^5.2.7
1283 "@sophie/shared": "workspace:*" 1291 "@sophie/shared": "workspace:*"
1284 "@types/lodash": ^4.14.178 1292 "@types/lodash-es": ^4.14.178
1285 "@types/react": ^17.0.38 1293 "@types/react": ^17.0.38
1286 "@types/react-dom": ^17.0.11 1294 "@types/react-dom": ^17.0.11
1287 "@vitejs/plugin-react": ^1.1.4 1295 "@vitejs/plugin-react": ^1.1.4
1288 lodash: ^4.17.21 1296 lodash-es: ^4.17.21
1297 loglevel: ^1.8.0
1298 loglevel-plugin-prefix: ^0.8.4
1289 mobx: ^6.3.12 1299 mobx: ^6.3.12
1290 mobx-react-lite: ^3.2.3 1300 mobx-react-lite: ^3.2.3
1291 mobx-state-tree: ^5.1.0 1301 mobx-state-tree: ^5.1.0
@@ -1293,8 +1303,6 @@ __metadata:
1293 react: ^17.0.2 1303 react: ^17.0.2
1294 react-dom: ^17.0.2 1304 react-dom: ^17.0.2
1295 remotedev: ^0.2.9 1305 remotedev: ^0.2.9
1296 rimraf: ^3.0.2
1297 typescript: ^4.5.4
1298 vite: ^2.7.10 1306 vite: ^2.7.10
1299 languageName: unknown 1307 languageName: unknown
1300 linkType: soft 1308 linkType: soft
@@ -1304,8 +1312,6 @@ __metadata:
1304 resolution: "@sophie/service-inject@workspace:packages/service-inject" 1312 resolution: "@sophie/service-inject@workspace:packages/service-inject"
1305 dependencies: 1313 dependencies:
1306 "@sophie/service-shared": "workspace:*" 1314 "@sophie/service-shared": "workspace:*"
1307 rimraf: ^3.0.2
1308 typescript: ^4.5.4
1309 languageName: unknown 1315 languageName: unknown
1310 linkType: soft 1316 linkType: soft
1311 1317
@@ -1315,8 +1321,8 @@ __metadata:
1315 dependencies: 1321 dependencies:
1316 "@sophie/service-shared": "workspace:*" 1322 "@sophie/service-shared": "workspace:*"
1317 electron: 16.0.6 1323 electron: 16.0.6
1318 rimraf: ^3.0.2 1324 loglevel: ^1.8.0
1319 typescript: ^4.5.4 1325 loglevel-plugin-prefix: ^0.8.4
1320 languageName: unknown 1326 languageName: unknown
1321 linkType: soft 1327 linkType: soft
1322 1328
@@ -1324,8 +1330,6 @@ __metadata:
1324 version: 0.0.0-use.local 1330 version: 0.0.0-use.local
1325 resolution: "@sophie/service-shared@workspace:packages/service-shared" 1331 resolution: "@sophie/service-shared@workspace:packages/service-shared"
1326 dependencies: 1332 dependencies:
1327 rimraf: ^3.0.2
1328 typescript: ^4.5.4
1329 zod: ^3.11.6 1333 zod: ^3.11.6
1330 languageName: unknown 1334 languageName: unknown
1331 linkType: soft 1335 linkType: soft
@@ -1336,8 +1340,6 @@ __metadata:
1336 dependencies: 1340 dependencies:
1337 mobx: ^6.3.12 1341 mobx: ^6.3.12
1338 mobx-state-tree: ^5.1.0 1342 mobx-state-tree: ^5.1.0
1339 rimraf: ^3.0.2
1340 typescript: ^4.5.4
1341 zod: ^3.11.6 1343 zod: ^3.11.6
1342 languageName: unknown 1344 languageName: unknown
1343 linkType: soft 1345 linkType: soft
@@ -1508,7 +1510,7 @@ __metadata:
1508 languageName: node 1510 languageName: node
1509 linkType: hard 1511 linkType: hard
1510 1512
1511"@types/lodash-es@npm:^4.17.5": 1513"@types/lodash-es@npm:^4.14.178, @types/lodash-es@npm:^4.17.5":
1512 version: 4.17.5 1514 version: 4.17.5
1513 resolution: "@types/lodash-es@npm:4.17.5" 1515 resolution: "@types/lodash-es@npm:4.17.5"
1514 dependencies: 1516 dependencies:
@@ -1517,7 +1519,7 @@ __metadata:
1517 languageName: node 1519 languageName: node
1518 linkType: hard 1520 linkType: hard
1519 1521
1520"@types/lodash@npm:*, @types/lodash@npm:^4.14.178": 1522"@types/lodash@npm:*":
1521 version: 4.14.178 1523 version: 4.14.178
1522 resolution: "@types/lodash@npm:4.14.178" 1524 resolution: "@types/lodash@npm:4.14.178"
1523 checksum: a69a04a60bfc5257c3130a554b4efa0c383f0141b7b3db8ab7cf07ad2a46ea085fce66d0242da41da7e5647b133d5dfb2c15add9cbed8d7fef955e4a1e5b3128 1525 checksum: a69a04a60bfc5257c3130a554b4efa0c383f0141b7b3db8ab7cf07ad2a46ea085fce66d0242da41da7e5647b133d5dfb2c15add9cbed8d7fef955e4a1e5b3128
@@ -2058,6 +2060,16 @@ __metadata:
2058 languageName: node 2060 languageName: node
2059 linkType: hard 2061 linkType: hard
2060 2062
2063"aria-query@npm:^4.2.2":
2064 version: 4.2.2
2065 resolution: "aria-query@npm:4.2.2"
2066 dependencies:
2067 "@babel/runtime": ^7.10.2
2068 "@babel/runtime-corejs3": ^7.10.2
2069 checksum: 38401a9a400f26f3dcc24b84997461a16b32869a9893d323602bed8da40a8bcc0243b8d2880e942249a1496cea7a7de769e93d21c0baa439f01e1ee936fed665
2070 languageName: node
2071 linkType: hard
2072
2061"array-includes@npm:^3.1.3, array-includes@npm:^3.1.4": 2073"array-includes@npm:^3.1.3, array-includes@npm:^3.1.4":
2062 version: 3.1.4 2074 version: 3.1.4
2063 resolution: "array-includes@npm:3.1.4" 2075 resolution: "array-includes@npm:3.1.4"
@@ -2125,6 +2137,13 @@ __metadata:
2125 languageName: node 2137 languageName: node
2126 linkType: hard 2138 linkType: hard
2127 2139
2140"ast-types-flow@npm:^0.0.7":
2141 version: 0.0.7
2142 resolution: "ast-types-flow@npm:0.0.7"
2143 checksum: a26dcc2182ffee111cad7c471759b0bda22d3b7ebacf27c348b22c55f16896b18ab0a4d03b85b4020dce7f3e634b8f00b593888f622915096ea1927fa51866c4
2144 languageName: node
2145 linkType: hard
2146
2128"astral-regex@npm:^2.0.0": 2147"astral-regex@npm:^2.0.0":
2129 version: 2.0.0 2148 version: 2.0.0
2130 resolution: "astral-regex@npm:2.0.0" 2149 resolution: "astral-regex@npm:2.0.0"
@@ -2167,6 +2186,20 @@ __metadata:
2167 languageName: node 2186 languageName: node
2168 linkType: hard 2187 linkType: hard
2169 2188
2189"axe-core@npm:^4.3.5":
2190 version: 4.3.5
2191 resolution: "axe-core@npm:4.3.5"
2192 checksum: 973c6a80f0aaa663820b209d4202de7a0c240a2dea2f3cff168b09c0f221b27179b1f0988f00ad11ed63cbc50535920f8ca779de1c60dc82090ab2d275f71fdd
2193 languageName: node
2194 linkType: hard
2195
2196"axobject-query@npm:^2.2.0":
2197 version: 2.2.0
2198 resolution: "axobject-query@npm:2.2.0"
2199 checksum: 96b8c7d807ca525f41ad9b286186e2089b561ba63a6d36c3e7d73dc08150714660995c7ad19cda05784458446a0793b45246db45894631e13853f48c1aa3117f
2200 languageName: node
2201 linkType: hard
2202
2170"babel-jest@npm:^27.4.6": 2203"babel-jest@npm:^27.4.6":
2171 version: 27.4.6 2204 version: 27.4.6
2172 resolution: "babel-jest@npm:27.4.6" 2205 resolution: "babel-jest@npm:27.4.6"
@@ -2855,6 +2888,13 @@ __metadata:
2855 languageName: node 2888 languageName: node
2856 linkType: hard 2889 linkType: hard
2857 2890
2891"core-js-pure@npm:^3.19.0":
2892 version: 3.20.2
2893 resolution: "core-js-pure@npm:3.20.2"
2894 checksum: d6b3f6782e3f2fc27eb2335917d5c5d0e7621e424c25da67429e9b48b7708b76fdc4a178b245421eeb8342c0ea9b0ca636ece002db3d0e68246a9d395d461ca7
2895 languageName: node
2896 linkType: hard
2897
2858"core-util-is@npm:1.0.2": 2898"core-util-is@npm:1.0.2":
2859 version: 1.0.2 2899 version: 1.0.2
2860 resolution: "core-util-is@npm:1.0.2" 2900 resolution: "core-util-is@npm:1.0.2"
@@ -2958,6 +2998,13 @@ __metadata:
2958 languageName: node 2998 languageName: node
2959 linkType: hard 2999 linkType: hard
2960 3000
3001"damerau-levenshtein@npm:^1.0.7":
3002 version: 1.0.7
3003 resolution: "damerau-levenshtein@npm:1.0.7"
3004 checksum: ec8161cb381523e0db9b5c9b64863736da3197808b6fdc4a3a2ca764c0b4357e9232a4c5592220fb18755a91240b8fee7b13ab1b269fbbdc5f68c36f0053aceb
3005 languageName: node
3006 linkType: hard
3007
2961"data-urls@npm:^2.0.0": 3008"data-urls@npm:^2.0.0":
2962 version: 2.0.0 3009 version: 2.0.0
2963 resolution: "data-urls@npm:2.0.0" 3010 resolution: "data-urls@npm:2.0.0"
@@ -3373,6 +3420,13 @@ __metadata:
3373 languageName: node 3420 languageName: node
3374 linkType: hard 3421 linkType: hard
3375 3422
3423"emoji-regex@npm:^9.2.2":
3424 version: 9.2.2
3425 resolution: "emoji-regex@npm:9.2.2"
3426 checksum: 8487182da74aabd810ac6d6f1994111dfc0e331b01271ae01ec1eb0ad7b5ecc2bbbbd2f053c05cb55a1ac30449527d819bbfbf0e3de1023db308cbcb47f86601
3427 languageName: node
3428 linkType: hard
3429
3376"encodeurl@npm:^1.0.2": 3430"encodeurl@npm:^1.0.2":
3377 version: 1.0.2 3431 version: 1.0.2
3378 resolution: "encodeurl@npm:1.0.2" 3432 resolution: "encodeurl@npm:1.0.2"
@@ -3929,6 +3983,35 @@ __metadata:
3929 languageName: node 3983 languageName: node
3930 linkType: hard 3984 linkType: hard
3931 3985
3986"eslint-config-airbnb@npm:^19.0.4":
3987 version: 19.0.4
3988 resolution: "eslint-config-airbnb@npm:19.0.4"
3989 dependencies:
3990 eslint-config-airbnb-base: ^15.0.0
3991 object.assign: ^4.1.2
3992 object.entries: ^1.1.5
3993 peerDependencies:
3994 eslint: ^7.32.0 || ^8.2.0
3995 eslint-plugin-import: ^2.25.3
3996 eslint-plugin-jsx-a11y: ^6.5.1
3997 eslint-plugin-react: ^7.28.0
3998 eslint-plugin-react-hooks: ^4.3.0
3999 checksum: 253178689c3c80eef2567e3aaf0612e18973bc9cf51d9be36074b5dd58210e8b6942200a424bcccbb81ac884e41303479ab09f251a2a97addc2de61efdc9576c
4000 languageName: node
4001 linkType: hard
4002
4003"eslint-formatter-gitlab@npm:^3.0.0":
4004 version: 3.0.0
4005 resolution: "eslint-formatter-gitlab@npm:3.0.0"
4006 dependencies:
4007 chalk: ^4.0.0
4008 js-yaml: ^4.0.0
4009 peerDependencies:
4010 eslint: ^5 || ^6 || ^7 || ^8
4011 checksum: 7730a003e5e9b2bde7a8cc473d8b377ac198be33a9e0f1dbc7e5ddf489232dc683b8aedd2726c0539e0d2e73b9769b2fcaf089ad90b75fc8564a0b4a9511d041
4012 languageName: node
4013 linkType: hard
4014
3932"eslint-import-resolver-node@npm:^0.3.6": 4015"eslint-import-resolver-node@npm:^0.3.6":
3933 version: 0.3.6 4016 version: 0.3.6
3934 resolution: "eslint-import-resolver-node@npm:0.3.6" 4017 resolution: "eslint-import-resolver-node@npm:0.3.6"
@@ -3939,6 +4022,22 @@ __metadata:
3939 languageName: node 4022 languageName: node
3940 linkType: hard 4023 linkType: hard
3941 4024
4025"eslint-import-resolver-typescript@npm:^2.5.0":
4026 version: 2.5.0
4027 resolution: "eslint-import-resolver-typescript@npm:2.5.0"
4028 dependencies:
4029 debug: ^4.3.1
4030 glob: ^7.1.7
4031 is-glob: ^4.0.1
4032 resolve: ^1.20.0
4033 tsconfig-paths: ^3.9.0
4034 peerDependencies:
4035 eslint: "*"
4036 eslint-plugin-import: "*"
4037 checksum: e507a0cb46a05f136b1416664c7cbe1b1178001417421ce5621f147e88c8973b5c9ee1554dbf0b79ae93f760d69f2796e1a880d562356a080e9e4ac1058206a3
4038 languageName: node
4039 linkType: hard
4040
3942"eslint-module-utils@npm:^2.7.2": 4041"eslint-module-utils@npm:^2.7.2":
3943 version: 2.7.2 4042 version: 2.7.2
3944 resolution: "eslint-module-utils@npm:2.7.2" 4043 resolution: "eslint-module-utils@npm:2.7.2"
@@ -3972,6 +4071,37 @@ __metadata:
3972 languageName: node 4071 languageName: node
3973 linkType: hard 4072 linkType: hard
3974 4073
4074"eslint-plugin-jsx-a11y@npm:^6.5.1":
4075 version: 6.5.1
4076 resolution: "eslint-plugin-jsx-a11y@npm:6.5.1"
4077 dependencies:
4078 "@babel/runtime": ^7.16.3
4079 aria-query: ^4.2.2
4080 array-includes: ^3.1.4
4081 ast-types-flow: ^0.0.7
4082 axe-core: ^4.3.5
4083 axobject-query: ^2.2.0
4084 damerau-levenshtein: ^1.0.7
4085 emoji-regex: ^9.2.2
4086 has: ^1.0.3
4087 jsx-ast-utils: ^3.2.1
4088 language-tags: ^1.0.5
4089 minimatch: ^3.0.4
4090 peerDependencies:
4091 eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
4092 checksum: 311ab993ed982d0cc7cb0ba02fbc4b36c4a94e9434f31e97f13c4d67e8ecb8aec36baecfd759ff70498846e7e11d7a197eb04c39ad64934baf3354712fd0bc9d
4093 languageName: node
4094 linkType: hard
4095
4096"eslint-plugin-react-hooks@npm:^4.3.0":
4097 version: 4.3.0
4098 resolution: "eslint-plugin-react-hooks@npm:4.3.0"
4099 peerDependencies:
4100 eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
4101 checksum: 0ba1566ba0780bbc75a5921f49188edf232db2085ab32c8d3889592f0db9d6fadc97fcf639775e0101dec6b5409ca3c803ec44213b90c8bacaf0bdf921871c2e
4102 languageName: node
4103 linkType: hard
4104
3975"eslint-plugin-react@npm:^7.28.0": 4105"eslint-plugin-react@npm:^7.28.0":
3976 version: 7.28.0 4106 version: 7.28.0
3977 resolution: "eslint-plugin-react@npm:7.28.0" 4107 resolution: "eslint-plugin-react@npm:7.28.0"
@@ -4558,7 +4688,7 @@ __metadata:
4558 languageName: node 4688 languageName: node
4559 linkType: hard 4689 linkType: hard
4560 4690
4561"glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": 4691"glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7":
4562 version: 7.2.0 4692 version: 7.2.0
4563 resolution: "glob@npm:7.2.0" 4693 resolution: "glob@npm:7.2.0"
4564 dependencies: 4694 dependencies:
@@ -5857,7 +5987,7 @@ __metadata:
5857 languageName: node 5987 languageName: node
5858 linkType: hard 5988 linkType: hard
5859 5989
5860"js-yaml@npm:^4.1.0": 5990"js-yaml@npm:^4.0.0, js-yaml@npm:^4.1.0":
5861 version: 4.1.0 5991 version: 4.1.0
5862 resolution: "js-yaml@npm:4.1.0" 5992 resolution: "js-yaml@npm:4.1.0"
5863 dependencies: 5993 dependencies:
@@ -6053,7 +6183,7 @@ __metadata:
6053 languageName: node 6183 languageName: node
6054 linkType: hard 6184 linkType: hard
6055 6185
6056"jsx-ast-utils@npm:^2.4.1 || ^3.0.0": 6186"jsx-ast-utils@npm:^2.4.1 || ^3.0.0, jsx-ast-utils@npm:^3.2.1":
6057 version: 3.2.1 6187 version: 3.2.1
6058 resolution: "jsx-ast-utils@npm:3.2.1" 6188 resolution: "jsx-ast-utils@npm:3.2.1"
6059 dependencies: 6189 dependencies:
@@ -6091,6 +6221,22 @@ __metadata:
6091 languageName: node 6221 languageName: node
6092 linkType: hard 6222 linkType: hard
6093 6223
6224"language-subtag-registry@npm:~0.3.2":
6225 version: 0.3.21
6226 resolution: "language-subtag-registry@npm:0.3.21"
6227 checksum: 5f794525a5bfcefeea155a681af1c03365b60e115b688952a53c6e0b9532b09163f57f1fcb69d6150e0e805ec0350644a4cb35da98f4902562915be9f89572a1
6228 languageName: node
6229 linkType: hard
6230
6231"language-tags@npm:^1.0.5":
6232 version: 1.0.5
6233 resolution: "language-tags@npm:1.0.5"
6234 dependencies:
6235 language-subtag-registry: ~0.3.2
6236 checksum: c81b5d8b9f5f9cfd06ee71ada6ddfe1cf83044dd5eeefcd1e420ad491944da8957688db4a0a9bc562df4afdc2783425cbbdfd152c01d93179cf86888903123cf
6237 languageName: node
6238 linkType: hard
6239
6094"latest-version@npm:^5.1.0": 6240"latest-version@npm:^5.1.0":
6095 version: 5.1.0 6241 version: 5.1.0
6096 resolution: "latest-version@npm:5.1.0" 6242 resolution: "latest-version@npm:5.1.0"
@@ -6190,7 +6336,7 @@ __metadata:
6190 languageName: node 6336 languageName: node
6191 linkType: hard 6337 linkType: hard
6192 6338
6193"lodash@npm:^4.17.10, lodash@npm:^4.17.15, lodash@npm:^4.17.21, lodash@npm:^4.7.0": 6339"lodash@npm:^4.17.10, lodash@npm:^4.17.15, lodash@npm:^4.7.0":
6194 version: 4.17.21 6340 version: 4.17.21
6195 resolution: "lodash@npm:4.17.21" 6341 resolution: "lodash@npm:4.17.21"
6196 checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 6342 checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7
@@ -7789,9 +7935,15 @@ __metadata:
7789 electron-builder: ^22.14.12 7935 electron-builder: ^22.14.12
7790 esbuild: ^0.14.11 7936 esbuild: ^0.14.11
7791 eslint: ^8.6.0 7937 eslint: ^8.6.0
7938 eslint-config-airbnb: ^19.0.4
7939 eslint-config-airbnb-base: ^15.0.0
7792 eslint-config-airbnb-typescript: ^16.1.0 7940 eslint-config-airbnb-typescript: ^16.1.0
7941 eslint-formatter-gitlab: ^3.0.0
7942 eslint-import-resolver-typescript: ^2.5.0
7793 eslint-plugin-import: ^2.25.4 7943 eslint-plugin-import: ^2.25.4
7944 eslint-plugin-jsx-a11y: ^6.5.1
7794 eslint-plugin-react: ^7.28.0 7945 eslint-plugin-react: ^7.28.0
7946 eslint-plugin-react-hooks: ^4.3.0
7795 git-repo-info: ^2.1.1 7947 git-repo-info: ^2.1.1
7796 jest: ^27.4.7 7948 jest: ^27.4.7
7797 preload: ^0.1.0 7949 preload: ^0.1.0
@@ -8224,7 +8376,7 @@ __metadata:
8224 languageName: node 8376 languageName: node
8225 linkType: hard 8377 linkType: hard
8226 8378
8227"tsconfig-paths@npm:^3.12.0": 8379"tsconfig-paths@npm:^3.12.0, tsconfig-paths@npm:^3.9.0":
8228 version: 3.12.0 8380 version: 3.12.0
8229 resolution: "tsconfig-paths@npm:3.12.0" 8381 resolution: "tsconfig-paths@npm:3.12.0"
8230 dependencies: 8382 dependencies: