diff options
author | Kristóf Marussy <kristof@marussy.com> | 2022-01-09 20:33:53 +0100 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2022-01-09 20:33:53 +0100 |
commit | d85f09cbed5f3d2501f791e689011ae127df1cbb (patch) | |
tree | 0ed5be55dd5d3dec1d51eb60e7ff229274030a57 | |
parent | build: Disable single-run eslint-typescript (diff) | |
download | sophie-d85f09cbed5f3d2501f791e689011ae127df1cbb.tar.gz sophie-d85f09cbed5f3d2501f791e689011ae127df1cbb.tar.zst sophie-d85f09cbed5f3d2501f791e689011ae127df1cbb.zip |
build: Add prettier
eslint will also enforce prettier rules, so there is no need to call
prettier separately in CI.
Signed-off-by: Kristóf Marussy <kristof@marussy.com>
68 files changed, 636 insertions, 540 deletions
diff --git a/.electron-builder.config.cjs b/.electron-builder.config.cjs index 9e2d11f..f406cc8 100644 --- a/.electron-builder.config.cjs +++ b/.electron-builder.config.cjs | |||
@@ -43,18 +43,20 @@ const config = { | |||
43 | */ | 43 | */ |
44 | async function burnFuses(context) { | 44 | async function burnFuses(context) { |
45 | /** @type {string} */ | 45 | /** @type {string} */ |
46 | const ext = { | 46 | const ext = |
47 | darwin: '.app', | 47 | { |
48 | win32: '.exe', | 48 | darwin: '.app', |
49 | }[context.electronPlatformName] || ''; | 49 | win32: '.exe', |
50 | }[context.electronPlatformName] || ''; | ||
50 | const electronBinaryPath = join( | 51 | const electronBinaryPath = join( |
51 | context.appOutDir, | 52 | context.appOutDir, |
52 | `${context.packager.appInfo.productFilename}${ext}` | 53 | `${context.packager.appInfo.productFilename}${ext}`, |
53 | ); | 54 | ); |
54 | /** @type {import('@electron/fuses').FuseConfig<boolean>} */ | 55 | /** @type {import('@electron/fuses').FuseConfig<boolean>} */ |
55 | const fuseConfig = { | 56 | const fuseConfig = { |
56 | version: FuseVersion.V1, | 57 | version: FuseVersion.V1, |
57 | resetAdHocDarwinSignature: context.electronPlatformName === 'darwin' && context.arch === Arch.arm64, | 58 | resetAdHocDarwinSignature: |
59 | context.electronPlatformName === 'darwin' && context.arch === Arch.arm64, | ||
58 | [FuseV1Options.RunAsNode]: false, | 60 | [FuseV1Options.RunAsNode]: false, |
59 | [FuseV1Options.EnableCookieEncryption]: true, | 61 | [FuseV1Options.EnableCookieEncryption]: true, |
60 | [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false, | 62 | [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false, |
diff --git a/.eslintignore b/.eslintignore index 01b8bcb..afc6ff1 100644 --- a/.eslintignore +++ b/.eslintignore | |||
@@ -22,8 +22,5 @@ coverage | |||
22 | .idea | 22 | .idea |
23 | npm-debug.log.* | 23 | npm-debug.log.* |
24 | .pnpm-debug.log* | 24 | .pnpm-debug.log* |
25 | *.css.d.ts | ||
26 | *.sass.d.ts | ||
27 | *.scss.d.ts | ||
28 | 25 | ||
29 | dist/ | 26 | dist/ |
diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 7b05d5d..1301de4 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs | |||
@@ -1,19 +1,15 @@ | |||
1 | const project = [ | 1 | const project = ['./tsconfig.json', './packages/*/tsconfig.json']; |
2 | './tsconfig.json', | ||
3 | './packages/*/tsconfig.json', | ||
4 | ]; | ||
5 | 2 | ||
6 | module.exports = { | 3 | module.exports = { |
7 | root: true, | 4 | root: true, |
8 | plugins: [ | 5 | plugins: ['@typescript-eslint'], |
9 | '@typescript-eslint', | ||
10 | ], | ||
11 | extends: [ | 6 | extends: [ |
12 | 'airbnb', | 7 | 'airbnb', |
13 | 'airbnb-typescript', | 8 | 'airbnb-typescript', |
14 | 'airbnb/hooks', | 9 | 'airbnb/hooks', |
15 | 'plugin:@typescript-eslint/recommended', | 10 | 'plugin:@typescript-eslint/recommended', |
16 | 'plugin:@typescript-eslint/recommended-requiring-type-checking', | 11 | 'plugin:@typescript-eslint/recommended-requiring-type-checking', |
12 | 'plugin:prettier/recommended', | ||
17 | ], | 13 | ], |
18 | env: { | 14 | env: { |
19 | es6: true, | 15 | es6: true, |
@@ -56,9 +52,7 @@ module.exports = { | |||
56 | }, | 52 | }, |
57 | overrides: [ | 53 | overrides: [ |
58 | { | 54 | { |
59 | files: [ | 55 | files: ['**/stores/**/*.ts'], |
60 | '**/stores/**/*.ts', | ||
61 | ], | ||
62 | rules: { | 56 | rules: { |
63 | // In a mobx-state-tree action, we assign to the properties of `self` to update the store. | 57 | // In a mobx-state-tree action, we assign to the properties of `self` to update the store. |
64 | 'no-param-reassign': 'off', | 58 | 'no-param-reassign': 'off', |
@@ -67,10 +61,7 @@ module.exports = { | |||
67 | }, | 61 | }, |
68 | }, | 62 | }, |
69 | { | 63 | { |
70 | files: [ | 64 | files: ['**/__tests__/*.{ts,tsx}', '**/*.{spec,test}.{ts,tsx}'], |
71 | '**/__tests__/*.{ts,tsx}', | ||
72 | '**/*.{spec,test}.{ts,tsx}', | ||
73 | ], | ||
74 | rules: { | 65 | rules: { |
75 | // If a non-null assertion fails in a test, the test will also fail anyways. | 66 | // If a non-null assertion fails in a test, the test will also fail anyways. |
76 | '@typescript-eslint/no-non-null-assertion': 'off', | 67 | '@typescript-eslint/no-non-null-assertion': 'off', |
@@ -79,21 +70,14 @@ module.exports = { | |||
79 | }, | 70 | }, |
80 | }, | 71 | }, |
81 | { | 72 | { |
82 | files: [ | 73 | files: ['**/*.js'], |
83 | '**/*.js', | ||
84 | ], | ||
85 | rules: { | 74 | rules: { |
86 | // ESM requires extensions for imports. | 75 | // ESM requires extensions for imports. |
87 | 'import/extensions': [ | 76 | 'import/extensions': ['error', 'ignorePackages'], |
88 | 'error', | ||
89 | 'ignorePackages', | ||
90 | ], | ||
91 | }, | 77 | }, |
92 | }, | 78 | }, |
93 | { | 79 | { |
94 | files: [ | 80 | files: ['**/*.cjs'], |
95 | '**/*.cjs', | ||
96 | ], | ||
97 | parserOptions: { | 81 | parserOptions: { |
98 | sourceType: 'script', | 82 | sourceType: 'script', |
99 | }, | 83 | }, |
@@ -102,10 +86,10 @@ module.exports = { | |||
102 | files: [ | 86 | files: [ |
103 | '.electron-builder.config.cjs', | 87 | '.electron-builder.config.cjs', |
104 | 'config/**/*.{cjs,js}', | 88 | 'config/**/*.{cjs,js}', |
105 | 'jest.config.js', | 89 | '*.config.{cjs,js}', |
106 | 'scripts/**/*.js', | 90 | 'scripts/**/*.js', |
107 | 'packages/*/.eslintrc.cjs', | 91 | 'packages/*/.eslintrc.cjs', |
108 | 'packages/*/*.config.js', | 92 | 'packages/*/*.config.{cjs,js}', |
109 | ], | 93 | ], |
110 | env: { | 94 | env: { |
111 | // Config files are never run in a browser (even in frontend projects). | 95 | // Config files are never run in a browser (even in frontend projects). |
@@ -126,9 +110,7 @@ module.exports = { | |||
126 | }, | 110 | }, |
127 | }, | 111 | }, |
128 | { | 112 | { |
129 | files: [ | 113 | files: ['packages/*/*.config.{cjs,js}'], |
130 | 'packages/*/*.config.js', | ||
131 | ], | ||
132 | rules: { | 114 | rules: { |
133 | // Allow relative imports of config files from the root package. | 115 | // Allow relative imports of config files from the root package. |
134 | 'import/no-relative-packages': 'off', | 116 | 'import/no-relative-packages': 'off', |
diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..afc6ff1 --- /dev/null +++ b/.prettierignore | |||
@@ -0,0 +1,26 @@ | |||
1 | # Logs | ||
2 | .log/ | ||
3 | logs | ||
4 | *.log | ||
5 | |||
6 | # Runtime data | ||
7 | pids | ||
8 | *.pid | ||
9 | *.seed | ||
10 | |||
11 | # Coverage directory used by tools like istanbul | ||
12 | coverage | ||
13 | .eslintcache | ||
14 | |||
15 | # Dependencies directories | ||
16 | .yarn | ||
17 | .vite | ||
18 | |||
19 | # OSX | ||
20 | .DS_Store | ||
21 | |||
22 | .idea | ||
23 | npm-debug.log.* | ||
24 | .pnpm-debug.log* | ||
25 | |||
26 | dist/ | ||
diff --git a/.yarnrc.yml b/.yarnrc.yml index e8caff1..7ffac8d 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml | |||
@@ -11,15 +11,15 @@ nodeLinker: node-modules | |||
11 | packageExtensions: | 11 | packageExtensions: |
12 | eslint-config-airbnb-typescript@*: | 12 | eslint-config-airbnb-typescript@*: |
13 | peerDependencies: | 13 | peerDependencies: |
14 | eslint: "*" | 14 | eslint: '*' |
15 | eslint-plugin-import: "*" | 15 | eslint-plugin-import: '*' |
16 | 16 | ||
17 | plugins: | 17 | plugins: |
18 | - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs | 18 | - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs |
19 | spec: "@yarnpkg/plugin-interactive-tools" | 19 | spec: '@yarnpkg/plugin-interactive-tools' |
20 | - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs | 20 | - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs |
21 | spec: "@yarnpkg/plugin-workspace-tools" | 21 | spec: '@yarnpkg/plugin-workspace-tools' |
22 | - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs | 22 | - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs |
23 | spec: "@yarnpkg/plugin-typescript" | 23 | spec: '@yarnpkg/plugin-typescript' |
24 | 24 | ||
25 | yarnPath: .yarn/releases/yarn-3.1.1.cjs | 25 | yarnPath: .yarn/releases/yarn-3.1.1.cjs |
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46ba3d6..e1d73d4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md | |||
@@ -10,29 +10,28 @@ Contributors are welcome to add their contact information to the license headers | |||
10 | > Everyone is permitted to copy and distribute verbatim copies of this | 10 | > Everyone is permitted to copy and distribute verbatim copies of this |
11 | > license document, but changing it is not allowed. | 11 | > license document, but changing it is not allowed. |
12 | > | 12 | > |
13 | > | ||
14 | > Developer's Certificate of Origin 1.1 | 13 | > Developer's Certificate of Origin 1.1 |
15 | > | 14 | > |
16 | > By making a contribution to this project, I certify that: | 15 | > By making a contribution to this project, I certify that: |
17 | > | 16 | > |
18 | > (a) The contribution was created in whole or in part by me and I | 17 | > (a) The contribution was created in whole or in part by me and I |
19 | > have the right to submit it under the open source license | 18 | > have the right to submit it under the open source license |
20 | > indicated in the file; or | 19 | > indicated in the file; or |
21 | > | 20 | > |
22 | > (b) The contribution is based upon previous work that, to the best | 21 | > (b) The contribution is based upon previous work that, to the best |
23 | > of my knowledge, is covered under an appropriate open source | 22 | > of my knowledge, is covered under an appropriate open source |
24 | > license and I have the right under that license to submit that | 23 | > license and I have the right under that license to submit that |
25 | > work with modifications, whether created in whole or in part | 24 | > work with modifications, whether created in whole or in part |
26 | > by me, under the same open source license (unless I am | 25 | > by me, under the same open source license (unless I am |
27 | > permitted to submit under a different license), as indicated | 26 | > permitted to submit under a different license), as indicated |
28 | > in the file; or | 27 | > in the file; or |
29 | > | 28 | > |
30 | > (c) The contribution was provided directly to me by some other | 29 | > (c) The contribution was provided directly to me by some other |
31 | > person who certified (a), (b) or (c) and I have not modified | 30 | > person who certified (a), (b) or (c) and I have not modified |
32 | > it. | 31 | > it. |
33 | > | 32 | > |
34 | > (d) I understand and agree that this project and the contribution | 33 | > (d) I understand and agree that this project and the contribution |
35 | > are public and that a record of the contribution (including all | 34 | > are public and that a record of the contribution (including all |
36 | > personal information I submit with it, including my sign-off) is | 35 | > personal information I submit with it, including my sign-off) is |
37 | > maintained indefinitely and may be redistributed consistent with | 36 | > maintained indefinitely and may be redistributed consistent with |
38 | > this project or the open source license(s) involved. | 37 | > this project or the open source license(s) involved. |
@@ -15,13 +15,13 @@ The project structure is based on [Vite Electron Builder Boilerplate](https://gi | |||
15 | 15 | ||
16 | After installing `node`, you can install `yarn` with | 16 | After installing `node`, you can install `yarn` with |
17 | 17 | ||
18 | ``` sh | 18 | ```sh |
19 | npm i -g yarn | 19 | npm i -g yarn |
20 | ``` | 20 | ``` |
21 | 21 | ||
22 | To start working, install all dependencies with | 22 | To start working, install all dependencies with |
23 | 23 | ||
24 | ``` sh | 24 | ```sh |
25 | yarn install --immutable | 25 | yarn install --immutable |
26 | ``` | 26 | ``` |
27 | 27 | ||
@@ -33,7 +33,7 @@ yarn types | |||
33 | 33 | ||
34 | To start a development instance of Sophie, which will reload on source changes, run | 34 | To start a development instance of Sophie, which will reload on source changes, run |
35 | 35 | ||
36 | ``` sh | 36 | ```sh |
37 | yarn watch | 37 | yarn watch |
38 | ``` | 38 | ``` |
39 | 39 | ||
@@ -51,7 +51,7 @@ yarn watch:test | |||
51 | 51 | ||
52 | To build the application in release mode, run | 52 | To build the application in release mode, run |
53 | 53 | ||
54 | ``` sh | 54 | ```sh |
55 | yarn compile | 55 | yarn compile |
56 | ``` | 56 | ``` |
57 | 57 | ||
@@ -69,8 +69,8 @@ yarn run lint | |||
69 | 69 | ||
70 | ## License | 70 | ## License |
71 | 71 | ||
72 | > Copyright (C) 2021-2022 Kristóf Marussy <kristof@marussy.com><br> | 72 | > Copyright (C) 2021-2022 Kristóf Marussy <kristof@marussy.com><br> |
73 | > Copyright (C) 2022 Vijay A <vraravam@users.noreply.github.com> | 73 | > Copyright (C) 2022 Vijay A <vraravam@users.noreply.github.com> |
74 | > | 74 | > |
75 | > This program is free software: you can redistribute it and/or modify | 75 | > This program is free software: you can redistribute it and/or modify |
76 | > it under the terms of the GNU Affero General Public License as | 76 | > it under the terms of the GNU Affero General Public License as |
@@ -78,7 +78,7 @@ yarn run lint | |||
78 | > | 78 | > |
79 | > This program is distributed in the hope that it will be useful, | 79 | > This program is distributed in the hope that it will be useful, |
80 | > but WITHOUT ANY WARRANTY; without even the implied warranty of | 80 | > but WITHOUT ANY WARRANTY; without even the implied warranty of |
81 | > MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 81 | > MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
82 | > GNU Affero General Public License for more details. | 82 | > GNU Affero General Public License for more details. |
83 | > You should have received a copy of the GNU Affero General Public License | 83 | > You should have received a copy of the GNU Affero General Public License |
84 | > along with this program. If not, see <[https://www.gnu.org/licenses/](https://www.gnu.org/licenses/)>. | 84 | > along with this program. If not, see <[https://www.gnu.org/licenses/](https://www.gnu.org/licenses/)>. |
diff --git a/config/buildConstants.js b/config/buildConstants.js index 9083d78..3c55500 100644 --- a/config/buildConstants.js +++ b/config/buildConstants.js | |||
@@ -7,11 +7,15 @@ const thisDir = fileURLToDirname(import.meta.url); | |||
7 | 7 | ||
8 | // We import this from a vite config, where top-level await is not available (es2021), | 8 | // We import this from a vite config, where top-level await is not available (es2021), |
9 | // so we have to use the synchronous filesystem API. | 9 | // so we have to use the synchronous filesystem API. |
10 | const electronVendorsJson = readFileSync(join(thisDir, '../.electron-vendors.cache.json'), 'utf8'); | 10 | const electronVendorsJson = readFileSync( |
11 | join(thisDir, '../.electron-vendors.cache.json'), | ||
12 | 'utf8', | ||
13 | ); | ||
11 | 14 | ||
12 | /** @type {{ chrome: number; node: number; }} */ | 15 | /** @type {{ chrome: number; node: number; }} */ |
13 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment | 16 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
14 | const { chrome: chromeVersion, node: nodeVersion } = JSON.parse(electronVendorsJson); | 17 | const { chrome: chromeVersion, node: nodeVersion } = |
18 | JSON.parse(electronVendorsJson); | ||
15 | 19 | ||
16 | /** @type {string} */ | 20 | /** @type {string} */ |
17 | export const banner = `/*! | 21 | export const banner = `/*! |
diff --git a/config/getEsbuildConfig.js b/config/getEsbuildConfig.js index b338d68..930f19f 100644 --- a/config/getEsbuildConfig.js +++ b/config/getEsbuildConfig.js | |||
@@ -25,7 +25,7 @@ export default function getEsbuildConfig(config, extraMetaEnvVars) { | |||
25 | js: banner, | 25 | js: banner, |
26 | }, | 26 | }, |
27 | ...config, | 27 | ...config, |
28 | sourcemap: isDevelopment ? (config.sourcemap || true) : false, | 28 | sourcemap: isDevelopment ? config.sourcemap || true : false, |
29 | define: { | 29 | define: { |
30 | 'process.env.NODE_ENV': modeString, | 30 | 'process.env.NODE_ENV': modeString, |
31 | 'process.env.MODE': modeString, | 31 | 'process.env.MODE': modeString, |
diff --git a/config/jest.config.base.js b/config/jest.config.base.js index 463e498..21f93be 100644 --- a/config/jest.config.base.js +++ b/config/jest.config.base.js | |||
@@ -9,22 +9,22 @@ export default { | |||
9 | transform: { | 9 | transform: { |
10 | '\\.tsx?$': join(thisDir, 'jestEsbuildTransformer.js'), | 10 | '\\.tsx?$': join(thisDir, 'jestEsbuildTransformer.js'), |
11 | }, | 11 | }, |
12 | extensionsToTreatAsEsm: [ | 12 | extensionsToTreatAsEsm: ['.ts', '.tsx'], |
13 | '.ts', | ||
14 | '.tsx', | ||
15 | ], | ||
16 | moduleNameMapper: { | 13 | moduleNameMapper: { |
17 | '^@sophie/(.+)$': join(thisDir, '../packages/$1/src/index.ts'), | 14 | '^@sophie/(.+)$': join(thisDir, '../packages/$1/src/index.ts'), |
18 | '^(\\.{1,2}/.*)\\.jsx?$': '$1', | 15 | '^(\\.{1,2}/.*)\\.jsx?$': '$1', |
19 | // Workaround for jest to recognize the vendored dependencies of chalk. | 16 | // Workaround for jest to recognize the vendored dependencies of chalk. |
20 | '^#ansi-styles$': join(thisDir, '../node_modules/chalk/source/vendor/ansi-styles/index.js'), | 17 | '^#ansi-styles$': join( |
21 | '^#supports-color$': join(thisDir, '../node_modules/chalk/source/vendor/supports-color/index.js'), | 18 | thisDir, |
19 | '../node_modules/chalk/source/vendor/ansi-styles/index.js', | ||
20 | ), | ||
21 | '^#supports-color$': join( | ||
22 | thisDir, | ||
23 | '../node_modules/chalk/source/vendor/supports-color/index.js', | ||
24 | ), | ||
22 | }, | 25 | }, |
23 | resetMocks: true, | 26 | resetMocks: true, |
24 | restoreMocks: true, | 27 | restoreMocks: true, |
25 | testEnvironment: 'node', | 28 | testEnvironment: 'node', |
26 | testPathIgnorePatterns: [ | 29 | testPathIgnorePatterns: ['/dist/', '/node_modules/'], |
27 | '/dist/', | ||
28 | '/node_modules/', | ||
29 | ], | ||
30 | }; | 30 | }; |
diff --git a/config/tsconfig.base.json b/config/tsconfig.base.json index 255f334..ff7594e 100644 --- a/config/tsconfig.base.json +++ b/config/tsconfig.base.json | |||
@@ -12,8 +12,6 @@ | |||
12 | "isolatedModules": true, | 12 | "isolatedModules": true, |
13 | "skipLibCheck": true, | 13 | "skipLibCheck": true, |
14 | "checkJs": true, | 14 | "checkJs": true, |
15 | "lib": [ | 15 | "lib": ["esnext"] |
16 | "esnext" | ||
17 | ] | ||
18 | } | 16 | } |
19 | } | 17 | } |
diff --git a/docs/architecture.md b/docs/architecture.md index 6467b6f..0122809 100644 --- a/docs/architecture.md +++ b/docs/architecture.md | |||
@@ -6,9 +6,9 @@ title: Architecture | |||
6 | 6 | ||
7 | Sophie is designed to | 7 | Sophie is designed to |
8 | 8 | ||
9 | * display _services_, i.e., web pages, most of which are messaging web applications, | 9 | - display _services_, i.e., web pages, most of which are messaging web applications, |
10 | * allow the user to quickly switch between services and save resources by displaying multiple services in the same desktop applications, | 10 | - allow the user to quickly switch between services and save resources by displaying multiple services in the same desktop applications, |
11 | * provide integrations for services, e.g., to manage unread message counts and notification in a single place. | 11 | - provide integrations for services, e.g., to manage unread message counts and notification in a single place. |
12 | 12 | ||
13 | Integrations for services are provided by pluggable _recipes_ to allow adding support for new services easily. | 13 | Integrations for services are provided by pluggable _recipes_ to allow adding support for new services easily. |
14 | A recipe may come directly from Sophie, a third party (under AGPLv3 licensing), or be created by the user themselves. | 14 | A recipe may come directly from Sophie, a third party (under AGPLv3 licensing), or be created by the user themselves. |
@@ -21,9 +21,9 @@ Sophie is built on the [Electron](https://www.electronjs.org/) application frame | |||
21 | 21 | ||
22 | Electron makes two facilities readily available to display foreign web pages: | 22 | Electron makes two facilities readily available to display foreign web pages: |
23 | 23 | ||
24 | * The easier solution that mimics the HTML `<iframe>` tag is [`<webview>`](https://www.electronjs.org/docs/latest/api/webview-tag/). | 24 | - The easier solution that mimics the HTML `<iframe>` tag is [`<webview>`](https://www.electronjs.org/docs/latest/api/webview-tag/). |
25 | However, it is deprecated and has some [critical bugs](https://github.com/electron/electron/issues/25469) when loading content with modern web security headers. | 25 | However, it is deprecated and has some [critical bugs](https://github.com/electron/electron/issues/25469) when loading content with modern web security headers. |
26 | * The new solution is the [`BrowserView`](https://www.electronjs.org/docs/latest/api/browser-view/), which is the approach taken by Sophie. | 26 | - The new solution is the [`BrowserView`](https://www.electronjs.org/docs/latest/api/browser-view/), which is the approach taken by Sophie. |
27 | The `BrowserView` is an overlay over the main browser window, so _Sophie will not be able render UI elements over foreign web pages_. | 27 | The `BrowserView` is an overlay over the main browser window, so _Sophie will not be able render UI elements over foreign web pages_. |
28 | While this is somewhat limiting, it allows us to take advantage of modern Electron APIs and coding practices. | 28 | While this is somewhat limiting, it allows us to take advantage of modern Electron APIs and coding practices. |
29 | 29 | ||
@@ -31,10 +31,10 @@ Electron makes two facilities readily available to display foreign web pages: | |||
31 | 31 | ||
32 | An Electron application is split into a _main process_ and a number of _renderer processes_: | 32 | An Electron application is split into a _main process_ and a number of _renderer processes_: |
33 | 33 | ||
34 | * The main process has access to NodeJS APIs for direct file system access. | 34 | - The main process has access to NodeJS APIs for direct file system access. |
35 | It is also the only process that can manipulate browser windows and `BrowserView` instances. | 35 | It is also the only process that can manipulate browser windows and `BrowserView` instances. |
36 | Therefore, most of Sophie's logic must be implemented in the main process. | 36 | Therefore, most of Sophie's logic must be implemented in the main process. |
37 | * The renderer processes are sandboxed processes responsible for rendering and executing web pages, both local and foreign. | 37 | - The renderer processes are sandboxed processes responsible for rendering and executing web pages, both local and foreign. |
38 | In turn, they are split into an _isolated world_ and a _main world_. | 38 | In turn, they are split into an _isolated world_ and a _main world_. |
39 | The isolated world (world id 999) can use [IPC](https://www.electronjs.org/docs/latest/api/ipc-renderer/) to communicate with the main process. | 39 | The isolated world (world id 999) can use [IPC](https://www.electronjs.org/docs/latest/api/ipc-renderer/) to communicate with the main process. |
40 | The only way to expose APIs to the main world is through the [`contextBridge`](https://www.electronjs.org/docs/latest/api/context-bridge). | 40 | The only way to expose APIs to the main world is through the [`contextBridge`](https://www.electronjs.org/docs/latest/api/context-bridge). |
diff --git a/jest.config.js b/jest.config.js index c631fdd..174322a 100644 --- a/jest.config.js +++ b/jest.config.js | |||
@@ -1,11 +1,7 @@ | |||
1 | /** @type {import('@jest/types').Config.InitialOptions} */ | 1 | /** @type {import('@jest/types').Config.InitialOptions} */ |
2 | export default { | 2 | export default { |
3 | projects: [ | 3 | projects: ['<rootDir>/packages/*'], |
4 | '<rootDir>/packages/*', | ||
5 | ], | ||
6 | /** @type {'babel' | 'v8'} */ | 4 | /** @type {'babel' | 'v8'} */ |
7 | coverageProvider: 'v8', | 5 | coverageProvider: 'v8', |
8 | collectCoverageFrom: [ | 6 | collectCoverageFrom: ['src/**/*.{ts,tsx}'], |
9 | 'src/**/*.{ts,tsx}', | ||
10 | ], | ||
11 | }; | 7 | }; |
diff --git a/package.json b/package.json index 1ca72f9..22cc738 100644 --- a/package.json +++ b/package.json | |||
@@ -51,6 +51,7 @@ | |||
51 | "devDependencies": { | 51 | "devDependencies": { |
52 | "@electron/fuses": "^1.5.0", | 52 | "@electron/fuses": "^1.5.0", |
53 | "@types/jest": "^27.4.0", | 53 | "@types/jest": "^27.4.0", |
54 | "@types/prettier": "^2", | ||
54 | "@typescript-eslint/eslint-plugin": "^5.9.0", | 55 | "@typescript-eslint/eslint-plugin": "^5.9.0", |
55 | "@typescript-eslint/parser": "^5.9.0", | 56 | "@typescript-eslint/parser": "^5.9.0", |
56 | "@vitejs/plugin-react": "^1.1.4", | 57 | "@vitejs/plugin-react": "^1.1.4", |
@@ -63,14 +64,17 @@ | |||
63 | "eslint-config-airbnb": "^19.0.4", | 64 | "eslint-config-airbnb": "^19.0.4", |
64 | "eslint-config-airbnb-base": "^15.0.0", | 65 | "eslint-config-airbnb-base": "^15.0.0", |
65 | "eslint-config-airbnb-typescript": "^16.1.0", | 66 | "eslint-config-airbnb-typescript": "^16.1.0", |
67 | "eslint-config-prettier": "^8.3.0", | ||
66 | "eslint-formatter-gitlab": "^3.0.0", | 68 | "eslint-formatter-gitlab": "^3.0.0", |
67 | "eslint-import-resolver-typescript": "^2.5.0", | 69 | "eslint-import-resolver-typescript": "^2.5.0", |
68 | "eslint-plugin-import": "^2.25.4", | 70 | "eslint-plugin-import": "^2.25.4", |
69 | "eslint-plugin-jsx-a11y": "^6.5.1", | 71 | "eslint-plugin-jsx-a11y": "^6.5.1", |
72 | "eslint-plugin-prettier": "^4.0.0", | ||
70 | "eslint-plugin-react": "^7.28.0", | 73 | "eslint-plugin-react": "^7.28.0", |
71 | "eslint-plugin-react-hooks": "^4.3.0", | 74 | "eslint-plugin-react-hooks": "^4.3.0", |
72 | "git-repo-info": "^2.1.1", | 75 | "git-repo-info": "^2.1.1", |
73 | "jest": "^27.4.7", | 76 | "jest": "^27.4.7", |
77 | "prettier": "^2.5.1", | ||
74 | "rimraf": "^3.0.2", | 78 | "rimraf": "^3.0.2", |
75 | "typescript": "^4.5.4", | 79 | "typescript": "^4.5.4", |
76 | "vite": "^2.7.10" | 80 | "vite": "^2.7.10" |
diff --git a/packages/main/esbuild.config.js b/packages/main/esbuild.config.js index 49fba6b..d5f6f1e 100644 --- a/packages/main/esbuild.config.js +++ b/packages/main/esbuild.config.js | |||
@@ -12,19 +12,20 @@ if (process.env.MODE !== 'development') { | |||
12 | 12 | ||
13 | const gitInfo = getRepoInfo(); | 13 | const gitInfo = getRepoInfo(); |
14 | 14 | ||
15 | export default getEsbuildConfig({ | 15 | export default getEsbuildConfig( |
16 | absWorkingDir: fileURLToDirname(import.meta.url), | 16 | { |
17 | entryPoints: [ | 17 | absWorkingDir: fileURLToDirname(import.meta.url), |
18 | 'src/index.ts', | 18 | entryPoints: ['src/index.ts'], |
19 | ], | 19 | outfile: 'dist/index.cjs', |
20 | outfile: 'dist/index.cjs', | 20 | format: 'cjs', |
21 | format: 'cjs', | 21 | platform: 'node', |
22 | platform: 'node', | 22 | target: node, |
23 | target: node, | 23 | external: externalPackages, |
24 | external: externalPackages, | 24 | }, |
25 | }, { | 25 | { |
26 | VITE_DEV_SERVER_URL: process.env.VITE_DEV_SERVER_URL || null, | 26 | VITE_DEV_SERVER_URL: process.env.VITE_DEV_SERVER_URL || null, |
27 | GIT_SHA: gitInfo.abbreviatedSha, | 27 | GIT_SHA: gitInfo.abbreviatedSha, |
28 | GIT_BRANCH: gitInfo.branch, | 28 | GIT_BRANCH: gitInfo.branch, |
29 | BUILD_DATE: new Date().getTime(), | 29 | BUILD_DATE: new Date().getTime(), |
30 | }); | 30 | }, |
31 | ); | ||
diff --git a/packages/main/src/controllers/__tests__/initConfig.spec.ts b/packages/main/src/controllers/__tests__/initConfig.spec.ts index e386a07..7b6d6ab 100644 --- a/packages/main/src/controllers/__tests__/initConfig.spec.ts +++ b/packages/main/src/controllers/__tests__/initConfig.spec.ts | |||
@@ -60,8 +60,12 @@ describe('when initializing', () => { | |||
60 | }); | 60 | }); |
61 | 61 | ||
62 | it('should bail if there is an an error creating the config file', async () => { | 62 | it('should bail if there is an an error creating the config file', async () => { |
63 | mocked(persistenceService.writeConfig).mockRejectedValue(new Error('boo')); | 63 | mocked(persistenceService.writeConfig).mockRejectedValue( |
64 | await expect(() => initConfig(config, persistenceService)).rejects.toBeInstanceOf(Error); | 64 | new Error('boo'), |
65 | ); | ||
66 | await expect(() => | ||
67 | initConfig(config, persistenceService), | ||
68 | ).rejects.toBeInstanceOf(Error); | ||
65 | }); | 69 | }); |
66 | }); | 70 | }); |
67 | 71 | ||
@@ -85,7 +89,9 @@ describe('when initializing', () => { | |||
85 | mocked(persistenceService.watchConfig).mockImplementationOnce(() => { | 89 | mocked(persistenceService.watchConfig).mockImplementationOnce(() => { |
86 | throw new Error('boo'); | 90 | throw new Error('boo'); |
87 | }); | 91 | }); |
88 | await expect(() => initConfig(config, persistenceService)).rejects.toBeInstanceOf(Error); | 92 | await expect(() => |
93 | initConfig(config, persistenceService), | ||
94 | ).rejects.toBeInstanceOf(Error); | ||
89 | }); | 95 | }); |
90 | }); | 96 | }); |
91 | 97 | ||
@@ -102,7 +108,9 @@ describe('when initializing', () => { | |||
102 | 108 | ||
103 | it('should bail if it cannot determine whether there is a config file', async () => { | 109 | it('should bail if it cannot determine whether there is a config file', async () => { |
104 | mocked(persistenceService.readConfig).mockRejectedValue(new Error('boo')); | 110 | mocked(persistenceService.readConfig).mockRejectedValue(new Error('boo')); |
105 | await expect(() => initConfig(config, persistenceService)).rejects.toBeInstanceOf(Error); | 111 | await expect(() => |
112 | initConfig(config, persistenceService), | ||
113 | ).rejects.toBeInstanceOf(Error); | ||
106 | }); | 114 | }); |
107 | }); | 115 | }); |
108 | 116 | ||
@@ -118,7 +126,9 @@ describe('when it has loaded the config', () => { | |||
118 | }); | 126 | }); |
119 | mocked(persistenceService.watchConfig).mockReturnValueOnce(watcherDisposer); | 127 | mocked(persistenceService.watchConfig).mockReturnValueOnce(watcherDisposer); |
120 | sutDisposer = await initConfig(config, persistenceService, throttleMs); | 128 | sutDisposer = await initConfig(config, persistenceService, throttleMs); |
121 | [[configChangedCallback]] = mocked(persistenceService.watchConfig).mock.calls; | 129 | [[configChangedCallback]] = mocked( |
130 | persistenceService.watchConfig, | ||
131 | ).mock.calls; | ||
122 | jest.resetAllMocks(); | 132 | jest.resetAllMocks(); |
123 | }); | 133 | }); |
124 | 134 | ||
diff --git a/packages/main/src/controllers/__tests__/initNativeTheme.spec.ts b/packages/main/src/controllers/__tests__/initNativeTheme.spec.ts index bd33f48..d4068af 100644 --- a/packages/main/src/controllers/__tests__/initNativeTheme.spec.ts +++ b/packages/main/src/controllers/__tests__/initNativeTheme.spec.ts | |||
@@ -58,14 +58,18 @@ it('should synchronize themeSource changes to the nativeTheme', () => { | |||
58 | }); | 58 | }); |
59 | 59 | ||
60 | it('should synchronize shouldUseDarkColors changes to the store', () => { | 60 | it('should synchronize shouldUseDarkColors changes to the store', () => { |
61 | const listener = mocked(nativeTheme.on).mock.calls.find(([event]) => event === 'updated')![1]; | 61 | const listener = mocked(nativeTheme.on).mock.calls.find( |
62 | ([event]) => event === 'updated', | ||
63 | )![1]; | ||
62 | shouldUseDarkColors = true; | 64 | shouldUseDarkColors = true; |
63 | listener(); | 65 | listener(); |
64 | expect(store.shared.shouldUseDarkColors).toBe(true); | 66 | expect(store.shared.shouldUseDarkColors).toBe(true); |
65 | }); | 67 | }); |
66 | 68 | ||
67 | it('should remove the listener on dispose', () => { | 69 | it('should remove the listener on dispose', () => { |
68 | const listener = mocked(nativeTheme.on).mock.calls.find(([event]) => event === 'updated')![1]; | 70 | const listener = mocked(nativeTheme.on).mock.calls.find( |
71 | ([event]) => event === 'updated', | ||
72 | )![1]; | ||
69 | disposeSut(); | 73 | disposeSut(); |
70 | expect(nativeTheme.off).toBeCalledWith('updated', listener); | 74 | expect(nativeTheme.off).toBeCalledWith('updated', listener); |
71 | }); | 75 | }); |
diff --git a/packages/main/src/controllers/initConfig.ts b/packages/main/src/controllers/initConfig.ts index 1d40762..e83b8da 100644 --- a/packages/main/src/controllers/initConfig.ts +++ b/packages/main/src/controllers/initConfig.ts | |||
@@ -59,20 +59,23 @@ export default async function initConfig( | |||
59 | lastSnapshotOnDisk = snapshot; | 59 | lastSnapshotOnDisk = snapshot; |
60 | } | 60 | } |
61 | 61 | ||
62 | if (!await readConfig()) { | 62 | if (!(await readConfig())) { |
63 | log.info('Config file was not found'); | 63 | log.info('Config file was not found'); |
64 | await writeConfig(); | 64 | await writeConfig(); |
65 | log.info('Created config file'); | 65 | log.info('Created config file'); |
66 | } | 66 | } |
67 | 67 | ||
68 | const disposeOnSnapshot = onSnapshot(config, debounce((snapshot) => { | 68 | const disposeOnSnapshot = onSnapshot( |
69 | // We can compare snapshots by reference, since it is only recreated on store changes. | 69 | config, |
70 | if (lastSnapshotOnDisk !== snapshot) { | 70 | debounce((snapshot) => { |
71 | writeConfig().catch((err) => { | 71 | // We can compare snapshots by reference, since it is only recreated on store changes. |
72 | log.error('Failed to write config on config change', err); | 72 | if (lastSnapshotOnDisk !== snapshot) { |
73 | }); | 73 | writeConfig().catch((err) => { |
74 | } | 74 | log.error('Failed to write config on config change', err); |
75 | }, debounceTime)); | 75 | }); |
76 | } | ||
77 | }, debounceTime), | ||
78 | ); | ||
76 | 79 | ||
77 | const disposeWatcher = persistenceService.watchConfig(async () => { | 80 | const disposeWatcher = persistenceService.watchConfig(async () => { |
78 | try { | 81 | try { |
diff --git a/packages/main/src/devTools.ts b/packages/main/src/devTools.ts index 0486c36..69f1514 100644 --- a/packages/main/src/devTools.ts +++ b/packages/main/src/devTools.ts | |||
@@ -52,18 +52,12 @@ export async function installDevToolsExtensions(): Promise<void> { | |||
52 | @typescript-eslint/no-var-requires | 52 | @typescript-eslint/no-var-requires |
53 | */ | 53 | */ |
54 | } = require('electron-devtools-installer') as typeof import('electron-devtools-installer'); | 54 | } = require('electron-devtools-installer') as typeof import('electron-devtools-installer'); |
55 | await installExtension( | 55 | await installExtension([REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS], { |
56 | [ | 56 | forceDownload: false, |
57 | REACT_DEVELOPER_TOOLS, | 57 | loadExtensionOptions: { |
58 | REDUX_DEVTOOLS, | 58 | allowFileAccess: true, |
59 | ], | ||
60 | { | ||
61 | forceDownload: false, | ||
62 | loadExtensionOptions: { | ||
63 | allowFileAccess: true, | ||
64 | }, | ||
65 | }, | 59 | }, |
66 | ); | 60 | }); |
67 | } | 61 | } |
68 | 62 | ||
69 | /** | 63 | /** |
diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts index bc10b4c..1f80e44 100644 --- a/packages/main/src/index.ts +++ b/packages/main/src/index.ts | |||
@@ -33,12 +33,7 @@ import { | |||
33 | MainToRendererIpcMessage, | 33 | MainToRendererIpcMessage, |
34 | RendererToMainIpcMessage, | 34 | RendererToMainIpcMessage, |
35 | } from '@sophie/shared'; | 35 | } from '@sophie/shared'; |
36 | import { | 36 | import { app, BrowserView, BrowserWindow, ipcMain } from 'electron'; |
37 | app, | ||
38 | BrowserView, | ||
39 | BrowserWindow, | ||
40 | ipcMain, | ||
41 | } from 'electron'; | ||
42 | import { ensureDirSync, readFile, readFileSync } from 'fs-extra'; | 37 | import { ensureDirSync, readFile, readFileSync } from 'fs-extra'; |
43 | import { autorun } from 'mobx'; | 38 | import { autorun } from 'mobx'; |
44 | import { getSnapshot, onPatch } from 'mobx-state-tree'; | 39 | import { getSnapshot, onPatch } from 'mobx-state-tree'; |
@@ -97,7 +92,9 @@ app.setAboutPanelOptions({ | |||
97 | `Node.js: ${process.versions.node}`, | 92 | `Node.js: ${process.versions.node}`, |
98 | `Platform: ${osName()}`, | 93 | `Platform: ${osName()}`, |
99 | `Arch: ${arch()}`, | 94 | `Arch: ${arch()}`, |
100 | `Build date: ${new Date(Number(import.meta.env.BUILD_DATE)).toLocaleString()}`, | 95 | `Build date: ${new Date( |
96 | Number(import.meta.env.BUILD_DATE), | ||
97 | ).toLocaleString()}`, | ||
101 | `Git SHA: ${import.meta.env.GIT_SHA}`, | 98 | `Git SHA: ${import.meta.env.GIT_SHA}`, |
102 | `Git branch: ${import.meta.env.GIT_BRANCH}`, | 99 | `Git branch: ${import.meta.env.GIT_BRANCH}`, |
103 | ].join('\n'), | 100 | ].join('\n'), |
@@ -123,11 +120,13 @@ const serviceInject: WebSource = { | |||
123 | let mainWindow: BrowserWindow | null = null; | 120 | let mainWindow: BrowserWindow | null = null; |
124 | 121 | ||
125 | const store = createMainStore(); | 122 | const store = createMainStore(); |
126 | init(store).then((disposeCompositionRoot) => { | 123 | init(store) |
127 | app.on('will-quit', disposeCompositionRoot); | 124 | .then((disposeCompositionRoot) => { |
128 | }).catch((err) => { | 125 | app.on('will-quit', disposeCompositionRoot); |
129 | log.log('Failed to initialize application', err); | 126 | }) |
130 | }); | 127 | .catch((err) => { |
128 | log.log('Failed to initialize application', err); | ||
129 | }); | ||
131 | 130 | ||
132 | const rendererBaseUrl = getResourceUrl('../renderer/'); | 131 | const rendererBaseUrl = getResourceUrl('../renderer/'); |
133 | function shouldCancelMainWindowRequest(url: string, method: string): boolean { | 132 | function shouldCancelMainWindowRequest(url: string, method: string): boolean { |
@@ -141,12 +140,20 @@ function shouldCancelMainWindowRequest(url: string, method: string): boolean { | |||
141 | return true; | 140 | return true; |
142 | } | 141 | } |
143 | if (isDevelopment) { | 142 | if (isDevelopment) { |
144 | if (DEVMODE_ALLOWED_URL_PREFIXES.some((prefix) => normalizedUrl.startsWith(prefix))) { | 143 | if ( |
144 | DEVMODE_ALLOWED_URL_PREFIXES.some((prefix) => | ||
145 | normalizedUrl.startsWith(prefix), | ||
146 | ) | ||
147 | ) { | ||
145 | return false; | 148 | return false; |
146 | } | 149 | } |
147 | if (import.meta.env.VITE_DEV_SERVER_URL !== undefined) { | 150 | if (import.meta.env.VITE_DEV_SERVER_URL !== undefined) { |
148 | const isHttp = normalizedUrl.startsWith(import.meta.env.VITE_DEV_SERVER_URL); | 151 | const isHttp = normalizedUrl.startsWith( |
149 | const isWs = normalizedUrl.startsWith(import.meta.env.VITE_DEV_SERVER_URL.replace(/^http:/, 'ws:')); | 152 | import.meta.env.VITE_DEV_SERVER_URL, |
153 | ); | ||
154 | const isWs = normalizedUrl.startsWith( | ||
155 | import.meta.env.VITE_DEV_SERVER_URL.replace(/^http:/, 'ws:'), | ||
156 | ); | ||
150 | return !isHttp && !isWs; | 157 | return !isHttp && !isWs; |
151 | } | 158 | } |
152 | } | 159 | } |
@@ -168,19 +175,24 @@ async function createWindow(): Promise<unknown> { | |||
168 | 175 | ||
169 | webContents.userAgent = originalUserAgent; | 176 | webContents.userAgent = originalUserAgent; |
170 | 177 | ||
171 | webContents.session.setPermissionRequestHandler((_webContents, _permission, callback) => { | 178 | webContents.session.setPermissionRequestHandler( |
172 | callback(false); | 179 | (_webContents, _permission, callback) => { |
173 | }); | 180 | callback(false); |
181 | }, | ||
182 | ); | ||
174 | 183 | ||
175 | webContents.session.webRequest.onBeforeRequest(({ url, method }, callback) => { | 184 | webContents.session.webRequest.onBeforeRequest( |
176 | callback({ | 185 | ({ url, method }, callback) => { |
177 | cancel: shouldCancelMainWindowRequest(url, method), | 186 | callback({ |
178 | }); | 187 | cancel: shouldCancelMainWindowRequest(url, method), |
179 | }); | 188 | }); |
189 | }, | ||
190 | ); | ||
180 | 191 | ||
181 | const pageUrl = (isDevelopment && import.meta.env.VITE_DEV_SERVER_URL !== undefined) | 192 | const pageUrl = |
182 | ? import.meta.env.VITE_DEV_SERVER_URL | 193 | isDevelopment && import.meta.env.VITE_DEV_SERVER_URL !== undefined |
183 | : getResourceUrl('../renderer/dist/index.html'); | 194 | ? import.meta.env.VITE_DEV_SERVER_URL |
195 | : getResourceUrl('../renderer/dist/index.html'); | ||
184 | 196 | ||
185 | webContents.on('will-navigate', (event, url) => { | 197 | webContents.on('will-navigate', (event, url) => { |
186 | if (url !== pageUrl) { | 198 | if (url !== pageUrl) { |
@@ -273,9 +285,8 @@ async function createWindow(): Promise<unknown> { | |||
273 | webContents.send(MainToRendererIpcMessage.SharedStorePatch, patch); | 285 | webContents.send(MainToRendererIpcMessage.SharedStorePatch, patch); |
274 | }); | 286 | }); |
275 | 287 | ||
276 | ipcMain.handle( | 288 | ipcMain.handle(ServiceToMainIpcMessage.ApiExposedInMainWorld, (event) => |
277 | ServiceToMainIpcMessage.ApiExposedInMainWorld, | 289 | event.sender.id === browserView.webContents.id ? serviceInject : null, |
278 | (event) => (event.sender.id === browserView.webContents.id ? serviceInject : null), | ||
279 | ); | 290 | ); |
280 | 291 | ||
281 | browserView.webContents.on('ipc-message', (_event, channel, ...args) => { | 292 | browserView.webContents.on('ipc-message', (_event, channel, ...args) => { |
@@ -305,7 +316,9 @@ async function createWindow(): Promise<unknown> { | |||
305 | 316 | ||
306 | browserView.webContents.session.webRequest.onBeforeSendHeaders( | 317 | browserView.webContents.session.webRequest.onBeforeSendHeaders( |
307 | ({ url, requestHeaders }, callback) => { | 318 | ({ url, requestHeaders }, callback) => { |
308 | const requestUserAgent = url.match(/^[^:]+:\/\/accounts\.google\.[^./]+\//) | 319 | const requestUserAgent = url.match( |
320 | /^[^:]+:\/\/accounts\.google\.[^./]+\//, | ||
321 | ) | ||
309 | ? chromelessUserAgent | 322 | ? chromelessUserAgent |
310 | : userAgent; | 323 | : userAgent; |
311 | callback({ | 324 | callback({ |
@@ -317,9 +330,11 @@ async function createWindow(): Promise<unknown> { | |||
317 | }, | 330 | }, |
318 | ); | 331 | ); |
319 | 332 | ||
320 | browserView.webContents.loadURL('https://gitlab.com/say-hi-to-sophie/sophie').catch((err) => { | 333 | browserView.webContents |
321 | log.error('Failed to load browser', err); | 334 | .loadURL('https://gitlab.com/say-hi-to-sophie/sophie') |
322 | }); | 335 | .catch((err) => { |
336 | log.error('Failed to load browser', err); | ||
337 | }); | ||
323 | 338 | ||
324 | return mainWindow.loadURL(pageUrl); | 339 | return mainWindow.loadURL(pageUrl); |
325 | } | 340 | } |
@@ -342,17 +357,20 @@ app.on('window-all-closed', () => { | |||
342 | } | 357 | } |
343 | }); | 358 | }); |
344 | 359 | ||
345 | app.whenReady().then(async () => { | 360 | app |
346 | if (isDevelopment) { | 361 | .whenReady() |
347 | try { | 362 | .then(async () => { |
348 | await installDevToolsExtensions(); | 363 | if (isDevelopment) { |
349 | } catch (err) { | 364 | try { |
350 | log.error('Failed to install devtools extensions', err); | 365 | await installDevToolsExtensions(); |
366 | } catch (err) { | ||
367 | log.error('Failed to install devtools extensions', err); | ||
368 | } | ||
351 | } | 369 | } |
352 | } | ||
353 | 370 | ||
354 | return createWindow(); | 371 | return createWindow(); |
355 | }).catch((err) => { | 372 | }) |
356 | log.error('Failed to create window', err); | 373 | .catch((err) => { |
357 | process.exit(1); | 374 | log.error('Failed to create window', err); |
358 | }); | 375 | process.exit(1); |
376 | }); | ||
diff --git a/packages/main/src/init.ts b/packages/main/src/init.ts index 4487cc4..f3794bb 100644 --- a/packages/main/src/init.ts +++ b/packages/main/src/init.ts | |||
@@ -27,8 +27,13 @@ import { MainStore } from './stores/MainStore'; | |||
27 | import type Disposer from './utils/Disposer'; | 27 | import type Disposer from './utils/Disposer'; |
28 | 28 | ||
29 | export default async function init(store: MainStore): Promise<Disposer> { | 29 | export default async function init(store: MainStore): Promise<Disposer> { |
30 | const configPersistenceService = new ConfigPersistenceServiceImpl(app.getPath('userData')); | 30 | const configPersistenceService = new ConfigPersistenceServiceImpl( |
31 | const disposeConfigController = await initConfig(store.config, configPersistenceService); | 31 | app.getPath('userData'), |
32 | ); | ||
33 | const disposeConfigController = await initConfig( | ||
34 | store.config, | ||
35 | configPersistenceService, | ||
36 | ); | ||
32 | const disposeNativeThemeController = initNativeTheme(store); | 37 | const disposeNativeThemeController = initNativeTheme(store); |
33 | 38 | ||
34 | return () => { | 39 | return () => { |
diff --git a/packages/main/src/services/ConfigPersistenceService.ts b/packages/main/src/services/ConfigPersistenceService.ts index 7d508c5..ee5696d 100644 --- a/packages/main/src/services/ConfigPersistenceService.ts +++ b/packages/main/src/services/ConfigPersistenceService.ts | |||
@@ -21,7 +21,9 @@ | |||
21 | import type { ConfigSnapshotOut } from '../stores/Config'; | 21 | import type { ConfigSnapshotOut } from '../stores/Config'; |
22 | import type Disposer from '../utils/Disposer'; | 22 | import type Disposer from '../utils/Disposer'; |
23 | 23 | ||
24 | export type ReadConfigResult = { found: true; data: unknown; } | { found: false; }; | 24 | export type ReadConfigResult = |
25 | | { found: true; data: unknown } | ||
26 | | { found: false }; | ||
25 | 27 | ||
26 | export default interface ConfigPersistenceService { | 28 | export default interface ConfigPersistenceService { |
27 | readConfig(): Promise<ReadConfigResult>; | 29 | readConfig(): Promise<ReadConfigResult>; |
diff --git a/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts b/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts index df8c807..e92f706 100644 --- a/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts +++ b/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts | |||
@@ -32,7 +32,9 @@ import type { ReadConfigResult } from '../ConfigPersistenceService'; | |||
32 | 32 | ||
33 | const log = getLogger('configPersistence'); | 33 | const log = getLogger('configPersistence'); |
34 | 34 | ||
35 | export default class ConfigPersistenceServiceImpl implements ConfigPersistenceService { | 35 | export default class ConfigPersistenceServiceImpl |
36 | implements ConfigPersistenceService | ||
37 | { | ||
36 | private readonly configFilePath: string; | 38 | private readonly configFilePath: string; |
37 | 39 | ||
38 | private writingConfig = false; | 40 | private writingConfig = false; |
@@ -92,13 +94,19 @@ export default class ConfigPersistenceServiceImpl implements ConfigPersistenceSe | |||
92 | log.trace('Config file last modified at', mtime); | 94 | log.trace('Config file last modified at', mtime); |
93 | } catch (err) { | 95 | } catch (err) { |
94 | if ((err as NodeJS.ErrnoException).code === 'ENOENT') { | 96 | if ((err as NodeJS.ErrnoException).code === 'ENOENT') { |
95 | log.debug('Config file', this.configFilePath, 'was deleted after being changed'); | 97 | log.debug( |
98 | 'Config file', | ||
99 | this.configFilePath, | ||
100 | 'was deleted after being changed', | ||
101 | ); | ||
96 | return; | 102 | return; |
97 | } | 103 | } |
98 | throw err; | 104 | throw err; |
99 | } | 105 | } |
100 | if (!this.writingConfig | 106 | if ( |
101 | && (this.timeLastWritten === null || mtime > this.timeLastWritten)) { | 107 | !this.writingConfig && |
108 | (this.timeLastWritten === null || mtime > this.timeLastWritten) | ||
109 | ) { | ||
102 | log.debug( | 110 | log.debug( |
103 | 'Found a config file modified at', | 111 | 'Found a config file modified at', |
104 | mtime, | 112 | mtime, |
@@ -114,8 +122,10 @@ export default class ConfigPersistenceServiceImpl implements ConfigPersistenceSe | |||
114 | }); | 122 | }); |
115 | 123 | ||
116 | watcher.on('change', (eventType, filename) => { | 124 | watcher.on('change', (eventType, filename) => { |
117 | if (eventType === 'change' | 125 | if ( |
118 | && (filename === this.configFileName || filename === null)) { | 126 | eventType === 'change' && |
127 | (filename === this.configFileName || filename === null) | ||
128 | ) { | ||
119 | configChanged()?.catch((err) => { | 129 | configChanged()?.catch((err) => { |
120 | log.error('Unhandled error while listening for config changes', err); | 130 | log.error('Unhandled error while listening for config changes', err); |
121 | }); | 131 | }); |
diff --git a/packages/main/src/stores/MainStore.ts b/packages/main/src/stores/MainStore.ts index 7b26c52..eaf5b3c 100644 --- a/packages/main/src/stores/MainStore.ts +++ b/packages/main/src/stores/MainStore.ts | |||
@@ -24,26 +24,32 @@ import { applySnapshot, Instance, types } from 'mobx-state-tree'; | |||
24 | import type { Config } from './Config.js'; | 24 | import type { Config } from './Config.js'; |
25 | import { sharedStore } from './SharedStore'; | 25 | import { sharedStore } from './SharedStore'; |
26 | 26 | ||
27 | export const mainStore = types.model('MainStore', { | 27 | export const mainStore = types |
28 | browserViewBounds: types.optional(types.model('BrowserViewBounds', { | 28 | .model('MainStore', { |
29 | x: 0, | 29 | browserViewBounds: types.optional( |
30 | y: 0, | 30 | types.model('BrowserViewBounds', { |
31 | width: 0, | 31 | x: 0, |
32 | height: 0, | 32 | y: 0, |
33 | }), {}), | 33 | width: 0, |
34 | shared: types.optional(sharedStore, {}), | 34 | height: 0, |
35 | }).views((self) => ({ | 35 | }), |
36 | get config(): Config { | 36 | {}, |
37 | return self.shared.config; | 37 | ), |
38 | }, | 38 | shared: types.optional(sharedStore, {}), |
39 | })).actions((self) => ({ | 39 | }) |
40 | setBrowserViewBounds(bounds: BrowserViewBounds): void { | 40 | .views((self) => ({ |
41 | applySnapshot(self.browserViewBounds, bounds); | 41 | get config(): Config { |
42 | }, | 42 | return self.shared.config; |
43 | setShouldUseDarkColors(shouldUseDarkColors: boolean): void { | 43 | }, |
44 | self.shared.shouldUseDarkColors = shouldUseDarkColors; | 44 | })) |
45 | }, | 45 | .actions((self) => ({ |
46 | })); | 46 | setBrowserViewBounds(bounds: BrowserViewBounds): void { |
47 | applySnapshot(self.browserViewBounds, bounds); | ||
48 | }, | ||
49 | setShouldUseDarkColors(shouldUseDarkColors: boolean): void { | ||
50 | self.shared.shouldUseDarkColors = shouldUseDarkColors; | ||
51 | }, | ||
52 | })); | ||
47 | 53 | ||
48 | export interface MainStore extends Instance<typeof mainStore> {} | 54 | export interface MainStore extends Instance<typeof mainStore> {} |
49 | 55 | ||
diff --git a/packages/main/src/stores/SharedStore.ts b/packages/main/src/stores/SharedStore.ts index c023fc7..73245cd 100644 --- a/packages/main/src/stores/SharedStore.ts +++ b/packages/main/src/stores/SharedStore.ts | |||
@@ -23,7 +23,10 @@ import { Instance, types } from 'mobx-state-tree'; | |||
23 | 23 | ||
24 | import { config } from './Config'; | 24 | import { config } from './Config'; |
25 | 25 | ||
26 | export type { SharedStoreSnapshotIn, SharedStoreSnapshotOut } from '@sophie/shared'; | 26 | export type { |
27 | SharedStoreSnapshotIn, | ||
28 | SharedStoreSnapshotOut, | ||
29 | } from '@sophie/shared'; | ||
27 | 30 | ||
28 | export const sharedStore = originalSharedStore.props({ | 31 | export const sharedStore = originalSharedStore.props({ |
29 | config: types.optional(config, {}), | 32 | config: types.optional(config, {}), |
diff --git a/packages/main/src/utils/log.ts b/packages/main/src/utils/log.ts index c704797..5218721 100644 --- a/packages/main/src/utils/log.ts +++ b/packages/main/src/utils/log.ts | |||
@@ -46,9 +46,10 @@ prefix.apply(loglevel, { | |||
46 | format(level, name, timestamp) { | 46 | format(level, name, timestamp) { |
47 | const levelColor = getColor(level); | 47 | const levelColor = getColor(level); |
48 | const timeStr = timestamp.toString(); | 48 | const timeStr = timestamp.toString(); |
49 | const nameStr = typeof name === 'undefined' | 49 | const nameStr = |
50 | ? levelColor(':') | 50 | typeof name === 'undefined' |
51 | : ` ${chalk.green(`${name}:`)}`; | 51 | ? levelColor(':') |
52 | : ` ${chalk.green(`${name}:`)}`; | ||
52 | return `${chalk.gray(`[${timeStr}]`)} ${levelColor(level)}${nameStr}`; | 53 | return `${chalk.gray(`[${timeStr}]`)} ${levelColor(level)}${nameStr}`; |
53 | }, | 54 | }, |
54 | }); | 55 | }); |
diff --git a/packages/main/tsconfig.json b/packages/main/tsconfig.json index bdf68f4..dad597d 100644 --- a/packages/main/tsconfig.json +++ b/packages/main/tsconfig.json | |||
@@ -2,10 +2,7 @@ | |||
2 | "extends": "../../config/tsconfig.base.json", | 2 | "extends": "../../config/tsconfig.base.json", |
3 | "compilerOptions": { | 3 | "compilerOptions": { |
4 | "noEmit": true, | 4 | "noEmit": true, |
5 | "types": [ | 5 | "types": ["@types/jest", "node"] |
6 | "@types/jest", | ||
7 | "node" | ||
8 | ] | ||
9 | }, | 6 | }, |
10 | "references": [ | 7 | "references": [ |
11 | { | 8 | { |
diff --git a/packages/main/types/importMeta.d.ts b/packages/main/types/importMeta.d.ts index e422c30..efcf48a 100644 --- a/packages/main/types/importMeta.d.ts +++ b/packages/main/types/importMeta.d.ts | |||
@@ -7,5 +7,5 @@ interface ImportMeta { | |||
7 | GIT_SHA: string; | 7 | GIT_SHA: string; |
8 | GIT_BRANCH: string; | 8 | GIT_BRANCH: string; |
9 | BUILD_DATE: number; | 9 | BUILD_DATE: number; |
10 | } | 10 | }; |
11 | } | 11 | } |
diff --git a/packages/preload/esbuild.config.js b/packages/preload/esbuild.config.js index 66f5e84..d888987 100644 --- a/packages/preload/esbuild.config.js +++ b/packages/preload/esbuild.config.js | |||
@@ -4,15 +4,11 @@ import getEsbuildConfig from '../../config/getEsbuildConfig.js'; | |||
4 | 4 | ||
5 | export default getEsbuildConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileURLToDirname(import.meta.url), |
7 | entryPoints: [ | 7 | entryPoints: ['src/index.ts'], |
8 | 'src/index.ts', | ||
9 | ], | ||
10 | outfile: 'dist/index.cjs', | 8 | outfile: 'dist/index.cjs', |
11 | format: 'cjs', | 9 | format: 'cjs', |
12 | platform: 'node', | 10 | platform: 'node', |
13 | target: chrome, | 11 | target: chrome, |
14 | sourcemap: 'inline', | 12 | sourcemap: 'inline', |
15 | external: [ | 13 | external: ['electron'], |
16 | 'electron', | ||
17 | ], | ||
18 | }); | 14 | }); |
diff --git a/packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts b/packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts index a38dbac..f63c3f6 100644 --- a/packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts +++ b/packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts | |||
@@ -40,9 +40,12 @@ jest.unstable_mockModule('electron', () => ({ | |||
40 | 40 | ||
41 | const { ipcRenderer } = await import('electron'); | 41 | const { ipcRenderer } = await import('electron'); |
42 | 42 | ||
43 | const { default: createSophieRenderer } = await import('../createSophieRenderer'); | 43 | const { default: createSophieRenderer } = await import( |
44 | '../createSophieRenderer' | ||
45 | ); | ||
44 | 46 | ||
45 | const event: Electron.IpcRendererEvent = null as unknown as Electron.IpcRendererEvent; | 47 | const event: Electron.IpcRendererEvent = |
48 | null as unknown as Electron.IpcRendererEvent; | ||
46 | 49 | ||
47 | const snapshot: SharedStoreSnapshotIn = { | 50 | const snapshot: SharedStoreSnapshotIn = { |
48 | shouldUseDarkColors: true, | 51 | shouldUseDarkColors: true, |
@@ -83,7 +86,10 @@ describe('createSophieRenderer', () => { | |||
83 | 86 | ||
84 | describe('SharedStoreConnector', () => { | 87 | describe('SharedStoreConnector', () => { |
85 | let sut: SophieRenderer; | 88 | let sut: SophieRenderer; |
86 | let onSharedStorePatch: (eventArg: Electron.IpcRendererEvent, patchArg: unknown) => void; | 89 | let onSharedStorePatch: ( |
90 | eventArg: Electron.IpcRendererEvent, | ||
91 | patchArg: unknown, | ||
92 | ) => void; | ||
87 | const listener = { | 93 | const listener = { |
88 | // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars | 94 | // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars |
89 | onSnapshot: jest.fn((_snapshot: SharedStoreSnapshotIn) => {}), | 95 | onSnapshot: jest.fn((_snapshot: SharedStoreSnapshotIn) => {}), |
@@ -102,22 +108,25 @@ describe('SharedStoreConnector', () => { | |||
102 | it('should request a snapshot from the main process', async () => { | 108 | it('should request a snapshot from the main process', async () => { |
103 | mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot); | 109 | mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot); |
104 | await sut.onSharedStoreChange(listener); | 110 | await sut.onSharedStoreChange(listener); |
105 | expect(ipcRenderer.invoke).toBeCalledWith(RendererToMainIpcMessage.GetSharedStoreSnapshot); | 111 | expect(ipcRenderer.invoke).toBeCalledWith( |
112 | RendererToMainIpcMessage.GetSharedStoreSnapshot, | ||
113 | ); | ||
106 | expect(listener.onSnapshot).toBeCalledWith(snapshot); | 114 | expect(listener.onSnapshot).toBeCalledWith(snapshot); |
107 | }); | 115 | }); |
108 | 116 | ||
109 | it('should catch IPC errors without exposing them', async () => { | 117 | it('should catch IPC errors without exposing them', async () => { |
110 | mocked(ipcRenderer.invoke).mockRejectedValue(new Error('s3cr3t')); | 118 | mocked(ipcRenderer.invoke).mockRejectedValue(new Error('s3cr3t')); |
111 | await expect(sut.onSharedStoreChange(listener)).rejects.not.toHaveProperty( | 119 | await expect( |
112 | 'message', | 120 | sut.onSharedStoreChange(listener), |
113 | expect.stringMatching(/s3cr3t/), | 121 | ).rejects.not.toHaveProperty('message', expect.stringMatching(/s3cr3t/)); |
114 | ); | ||
115 | expect(listener.onSnapshot).not.toBeCalled(); | 122 | expect(listener.onSnapshot).not.toBeCalled(); |
116 | }); | 123 | }); |
117 | 124 | ||
118 | it('should not pass on invalid snapshots', async () => { | 125 | it('should not pass on invalid snapshots', async () => { |
119 | mocked(ipcRenderer.invoke).mockResolvedValueOnce(invalidSnapshot); | 126 | mocked(ipcRenderer.invoke).mockResolvedValueOnce(invalidSnapshot); |
120 | await expect(sut.onSharedStoreChange(listener)).rejects.toBeInstanceOf(Error); | 127 | await expect(sut.onSharedStoreChange(listener)).rejects.toBeInstanceOf( |
128 | Error, | ||
129 | ); | ||
121 | expect(listener.onSnapshot).not.toBeCalled(); | 130 | expect(listener.onSnapshot).not.toBeCalled(); |
122 | }); | 131 | }); |
123 | }); | 132 | }); |
@@ -125,7 +134,10 @@ describe('SharedStoreConnector', () => { | |||
125 | describe('dispatchAction', () => { | 134 | describe('dispatchAction', () => { |
126 | it('should dispatch valid actions', () => { | 135 | it('should dispatch valid actions', () => { |
127 | sut.dispatchAction(action); | 136 | sut.dispatchAction(action); |
128 | expect(ipcRenderer.send).toBeCalledWith(RendererToMainIpcMessage.DispatchAction, action); | 137 | expect(ipcRenderer.send).toBeCalledWith( |
138 | RendererToMainIpcMessage.DispatchAction, | ||
139 | action, | ||
140 | ); | ||
129 | }); | 141 | }); |
130 | 142 | ||
131 | it('should not dispatch invalid actions', () => { | 143 | it('should not dispatch invalid actions', () => { |
@@ -142,7 +154,9 @@ describe('SharedStoreConnector', () => { | |||
142 | 154 | ||
143 | function itRefusesToRegisterAnotherListener(): void { | 155 | function itRefusesToRegisterAnotherListener(): void { |
144 | it('should refuse to register another listener', async () => { | 156 | it('should refuse to register another listener', async () => { |
145 | await expect(sut.onSharedStoreChange(listener)).rejects.toBeInstanceOf(Error); | 157 | await expect(sut.onSharedStoreChange(listener)).rejects.toBeInstanceOf( |
158 | Error, | ||
159 | ); | ||
146 | }); | 160 | }); |
147 | } | 161 | } |
148 | 162 | ||
@@ -167,7 +181,9 @@ describe('SharedStoreConnector', () => { | |||
167 | }); | 181 | }); |
168 | 182 | ||
169 | it('should catch listener errors', () => { | 183 | it('should catch listener errors', () => { |
170 | mocked(listener.onPatch).mockImplementation(() => { throw new Error(); }); | 184 | mocked(listener.onPatch).mockImplementation(() => { |
185 | throw new Error(); | ||
186 | }); | ||
171 | onSharedStorePatch(event, patch); | 187 | onSharedStorePatch(event, patch); |
172 | }); | 188 | }); |
173 | 189 | ||
@@ -175,7 +191,9 @@ describe('SharedStoreConnector', () => { | |||
175 | 191 | ||
176 | describe('after the listener threw in onPatch', () => { | 192 | describe('after the listener threw in onPatch', () => { |
177 | beforeEach(() => { | 193 | beforeEach(() => { |
178 | mocked(listener.onPatch).mockImplementation(() => { throw new Error(); }); | 194 | mocked(listener.onPatch).mockImplementation(() => { |
195 | throw new Error(); | ||
196 | }); | ||
179 | onSharedStorePatch(event, patch); | 197 | onSharedStorePatch(event, patch); |
180 | listener.onPatch.mockRestore(); | 198 | listener.onPatch.mockRestore(); |
181 | }); | 199 | }); |
@@ -217,7 +235,9 @@ describe('SharedStoreConnector', () => { | |||
217 | describe('when a listener failed to register due to listener error', () => { | 235 | describe('when a listener failed to register due to listener error', () => { |
218 | beforeEach(async () => { | 236 | beforeEach(async () => { |
219 | mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot); | 237 | mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot); |
220 | mocked(listener.onSnapshot).mockImplementation(() => { throw new Error(); }); | 238 | mocked(listener.onSnapshot).mockImplementation(() => { |
239 | throw new Error(); | ||
240 | }); | ||
221 | try { | 241 | try { |
222 | await sut.onSharedStoreChange(listener); | 242 | await sut.onSharedStoreChange(listener); |
223 | } catch { | 243 | } catch { |
@@ -236,15 +256,17 @@ describe('SharedStoreConnector', () => { | |||
236 | }; | 256 | }; |
237 | const listener2 = { | 257 | const listener2 = { |
238 | // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars | 258 | // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars |
239 | onSnapshot: jest.fn((_snapshot: SharedStoreSnapshotIn) => { }), | 259 | onSnapshot: jest.fn((_snapshot: SharedStoreSnapshotIn) => {}), |
240 | // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars | 260 | // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars |
241 | onPatch: jest.fn((_patch: IJsonPatch) => { }), | 261 | onPatch: jest.fn((_patch: IJsonPatch) => {}), |
242 | }; | 262 | }; |
243 | 263 | ||
244 | it('should fetch a second snapshot', async () => { | 264 | it('should fetch a second snapshot', async () => { |
245 | mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot2); | 265 | mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot2); |
246 | await sut.onSharedStoreChange(listener2); | 266 | await sut.onSharedStoreChange(listener2); |
247 | expect(ipcRenderer.invoke).toBeCalledWith(RendererToMainIpcMessage.GetSharedStoreSnapshot); | 267 | expect(ipcRenderer.invoke).toBeCalledWith( |
268 | RendererToMainIpcMessage.GetSharedStoreSnapshot, | ||
269 | ); | ||
248 | expect(listener2.onSnapshot).toBeCalledWith(snapshot2); | 270 | expect(listener2.onSnapshot).toBeCalledWith(snapshot2); |
249 | }); | 271 | }); |
250 | 272 | ||
diff --git a/packages/preload/src/contextBridge/createSophieRenderer.ts b/packages/preload/src/contextBridge/createSophieRenderer.ts index 2055080..b97503d 100644 --- a/packages/preload/src/contextBridge/createSophieRenderer.ts +++ b/packages/preload/src/contextBridge/createSophieRenderer.ts | |||
@@ -37,15 +37,18 @@ class SharedStoreConnector { | |||
37 | private listener: SharedStoreListener | null = null; | 37 | private listener: SharedStoreListener | null = null; |
38 | 38 | ||
39 | constructor(private readonly allowReplaceListener: boolean) { | 39 | constructor(private readonly allowReplaceListener: boolean) { |
40 | ipcRenderer.on(MainToRendererIpcMessage.SharedStorePatch, (_event, patch) => { | 40 | ipcRenderer.on( |
41 | try { | 41 | MainToRendererIpcMessage.SharedStorePatch, |
42 | // `mobx-state-tree` will validate the patch, so we can safely cast here. | 42 | (_event, patch) => { |
43 | this.listener?.onPatch(patch as IJsonPatch); | 43 | try { |
44 | } catch (err) { | 44 | // `mobx-state-tree` will validate the patch, so we can safely cast here. |
45 | log.error('Shared store listener onPatch failed', err); | 45 | this.listener?.onPatch(patch as IJsonPatch); |
46 | this.listener = null; | 46 | } catch (err) { |
47 | } | 47 | log.error('Shared store listener onPatch failed', err); |
48 | }); | 48 | this.listener = null; |
49 | } | ||
50 | }, | ||
51 | ); | ||
49 | } | 52 | } |
50 | 53 | ||
51 | async onSharedStoreChange(listener: SharedStoreListener): Promise<void> { | 54 | async onSharedStoreChange(listener: SharedStoreListener): Promise<void> { |
@@ -56,7 +59,9 @@ class SharedStoreConnector { | |||
56 | let success = false; | 59 | let success = false; |
57 | let snapshot: unknown | null = null; | 60 | let snapshot: unknown | null = null; |
58 | try { | 61 | try { |
59 | snapshot = await ipcRenderer.invoke(RendererToMainIpcMessage.GetSharedStoreSnapshot); | 62 | snapshot = await ipcRenderer.invoke( |
63 | RendererToMainIpcMessage.GetSharedStoreSnapshot, | ||
64 | ); | ||
60 | success = true; | 65 | success = true; |
61 | } catch (err) { | 66 | } catch (err) { |
62 | log.error('Failed to get initial shared store snapshot', err); | 67 | log.error('Failed to get initial shared store snapshot', err); |
@@ -87,7 +92,9 @@ function dispatchAction(actionToDispatch: Action): void { | |||
87 | } | 92 | } |
88 | } | 93 | } |
89 | 94 | ||
90 | export default function createSophieRenderer(allowReplaceListener: boolean): SophieRenderer { | 95 | export default function createSophieRenderer( |
96 | allowReplaceListener: boolean, | ||
97 | ): SophieRenderer { | ||
91 | const connector = new SharedStoreConnector(allowReplaceListener); | 98 | const connector = new SharedStoreConnector(allowReplaceListener); |
92 | return { | 99 | return { |
93 | onSharedStoreChange: connector.onSharedStoreChange.bind(connector), | 100 | onSharedStoreChange: connector.onSharedStoreChange.bind(connector), |
diff --git a/packages/preload/tsconfig.json b/packages/preload/tsconfig.json index 0cb1390..18c72b4 100644 --- a/packages/preload/tsconfig.json +++ b/packages/preload/tsconfig.json | |||
@@ -2,14 +2,8 @@ | |||
2 | "extends": "../../config/tsconfig.base.json", | 2 | "extends": "../../config/tsconfig.base.json", |
3 | "compilerOptions": { | 3 | "compilerOptions": { |
4 | "noEmit": true, | 4 | "noEmit": true, |
5 | "lib": [ | 5 | "lib": ["dom", "dom.iterable", "esnext"], |
6 | "dom", | 6 | "types": ["@types/jest"] |
7 | "dom.iterable", | ||
8 | "esnext" | ||
9 | ], | ||
10 | "types": [ | ||
11 | "@types/jest" | ||
12 | ] | ||
13 | }, | 7 | }, |
14 | "references": [ | 8 | "references": [ |
15 | { | 9 | { |
diff --git a/packages/preload/types/importMeta.d.ts b/packages/preload/types/importMeta.d.ts index 9b73170..ff3b17c 100644 --- a/packages/preload/types/importMeta.d.ts +++ b/packages/preload/types/importMeta.d.ts | |||
@@ -3,5 +3,5 @@ interface ImportMeta { | |||
3 | DEV: boolean; | 3 | DEV: boolean; |
4 | MODE: string; | 4 | MODE: string; |
5 | PROD: boolean; | 5 | PROD: boolean; |
6 | } | 6 | }; |
7 | } | 7 | } |
diff --git a/packages/renderer/.eslinrc.cjs b/packages/renderer/.eslinrc.cjs index 37d27ad..ee3cca6 100644 --- a/packages/renderer/.eslinrc.cjs +++ b/packages/renderer/.eslinrc.cjs | |||
@@ -5,10 +5,7 @@ module.exports = { | |||
5 | }, | 5 | }, |
6 | overrides: [ | 6 | overrides: [ |
7 | { | 7 | { |
8 | files: [ | 8 | files: ['.eslintrc.cjs', 'vite.config.js'], |
9 | '.eslintrc.cjs', | ||
10 | 'vite.config.js', | ||
11 | ], | ||
12 | env: { | 9 | env: { |
13 | browser: false, | 10 | browser: false, |
14 | node: true, | 11 | node: true, |
diff --git a/packages/renderer/index.html b/packages/renderer/index.html index 08469c7..039ce61 100644 --- a/packages/renderer/index.html +++ b/packages/renderer/index.html | |||
@@ -1,9 +1,12 @@ | |||
1 | <!DOCTYPE html> | 1 | <!DOCTYPE html> |
2 | <html lang="en"> | 2 | <html lang="en"> |
3 | <head> | 3 | <head> |
4 | <meta charset="UTF-8"> | 4 | <meta charset="UTF-8" /> |
5 | <meta http-equiv="Content-Security-Policy" content="script-src 'self' blob:"> | 5 | <meta |
6 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | 6 | http-equiv="Content-Security-Policy" |
7 | content="script-src 'self' blob:" | ||
8 | /> | ||
9 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
7 | <title>Sophie</title> | 10 | <title>Sophie</title> |
8 | </head> | 11 | </head> |
9 | <body> | 12 | <body> |
diff --git a/packages/renderer/src/components/BrowserViewPlaceholder.tsx b/packages/renderer/src/components/BrowserViewPlaceholder.tsx index 11c09d6..8f055e7 100644 --- a/packages/renderer/src/components/BrowserViewPlaceholder.tsx +++ b/packages/renderer/src/components/BrowserViewPlaceholder.tsx | |||
@@ -29,36 +29,37 @@ export default observer(() => { | |||
29 | const store = useStore(); | 29 | const store = useStore(); |
30 | 30 | ||
31 | // eslint-disable-next-line react-hooks/exhaustive-deps -- react-hooks doesn't support `throttle`. | 31 | // eslint-disable-next-line react-hooks/exhaustive-deps -- react-hooks doesn't support `throttle`. |
32 | const onResize = useCallback(throttle(([entry]: ResizeObserverEntry[]) => { | 32 | const onResize = useCallback( |
33 | if (entry) { | 33 | throttle(([entry]: ResizeObserverEntry[]) => { |
34 | const { | 34 | if (entry) { |
35 | x, | 35 | const { x, y, width, height } = entry.target.getBoundingClientRect(); |
36 | y, | 36 | store.setBrowserViewBounds({ |
37 | width, | 37 | x, |
38 | height, | 38 | y, |
39 | } = entry.target.getBoundingClientRect(); | 39 | width, |
40 | store.setBrowserViewBounds({ | 40 | height, |
41 | x, | 41 | }); |
42 | y, | 42 | } |
43 | width, | 43 | }, 100), |
44 | height, | 44 | [store], |
45 | }); | 45 | ); |
46 | } | ||
47 | }, 100), [store]); | ||
48 | 46 | ||
49 | const resizeObserverRef = useRef<ResizeObserver | null>(null); | 47 | const resizeObserverRef = useRef<ResizeObserver | null>(null); |
50 | 48 | ||
51 | const ref = useCallback((element: HTMLElement | null) => { | 49 | const ref = useCallback( |
52 | if (resizeObserverRef.current !== null) { | 50 | (element: HTMLElement | null) => { |
53 | resizeObserverRef.current.disconnect(); | 51 | if (resizeObserverRef.current !== null) { |
54 | } | 52 | resizeObserverRef.current.disconnect(); |
55 | if (element === null) { | 53 | } |
56 | resizeObserverRef.current = null; | 54 | if (element === null) { |
57 | return; | 55 | resizeObserverRef.current = null; |
58 | } | 56 | return; |
59 | resizeObserverRef.current = new ResizeObserver(onResize); | 57 | } |
60 | resizeObserverRef.current.observe(element); | 58 | resizeObserverRef.current = new ResizeObserver(onResize); |
61 | }, [onResize, resizeObserverRef]); | 59 | resizeObserverRef.current.observe(element); |
60 | }, | ||
61 | [onResize, resizeObserverRef], | ||
62 | ); | ||
62 | 63 | ||
63 | return ( | 64 | return ( |
64 | <Box | 65 | <Box |
diff --git a/packages/renderer/src/components/StoreProvider.tsx b/packages/renderer/src/components/StoreProvider.tsx index cde6a31..bb8495c 100644 --- a/packages/renderer/src/components/StoreProvider.tsx +++ b/packages/renderer/src/components/StoreProvider.tsx | |||
@@ -32,13 +32,14 @@ export function useStore(): RendererStore { | |||
32 | return store; | 32 | return store; |
33 | } | 33 | } |
34 | 34 | ||
35 | export default function StoreProvider({ children, store }: { | 35 | export default function StoreProvider({ |
36 | children: JSX.Element | JSX.Element[], | 36 | children, |
37 | store: RendererStore, | 37 | store, |
38 | }: { | ||
39 | children: JSX.Element | JSX.Element[]; | ||
40 | store: RendererStore; | ||
38 | }): JSX.Element { | 41 | }): JSX.Element { |
39 | return ( | 42 | return ( |
40 | <StoreContext.Provider value={store}> | 43 | <StoreContext.Provider value={store}>{children}</StoreContext.Provider> |
41 | {children} | ||
42 | </StoreContext.Provider> | ||
43 | ); | 44 | ); |
44 | } | 45 | } |
diff --git a/packages/renderer/src/components/ThemeProvider.tsx b/packages/renderer/src/components/ThemeProvider.tsx index eacaa52..3943371 100644 --- a/packages/renderer/src/components/ThemeProvider.tsx +++ b/packages/renderer/src/components/ThemeProvider.tsx | |||
@@ -27,20 +27,18 @@ import React from 'react'; | |||
27 | 27 | ||
28 | import { useStore } from './StoreProvider'; | 28 | import { useStore } from './StoreProvider'; |
29 | 29 | ||
30 | export default observer(({ children }: { | 30 | export default observer( |
31 | children: JSX.Element | JSX.Element[]; | 31 | ({ children }: { children: JSX.Element | JSX.Element[] }) => { |
32 | }) => { | 32 | const { |
33 | const { shared: { shouldUseDarkColors } } = useStore(); | 33 | shared: { shouldUseDarkColors }, |
34 | } = useStore(); | ||
34 | 35 | ||
35 | const theme = createTheme({ | 36 | const theme = createTheme({ |
36 | palette: { | 37 | palette: { |
37 | mode: shouldUseDarkColors ? 'dark' : 'light', | 38 | mode: shouldUseDarkColors ? 'dark' : 'light', |
38 | }, | 39 | }, |
39 | }); | 40 | }); |
40 | 41 | ||
41 | return ( | 42 | return <MuiThemeProvider theme={theme}>{children}</MuiThemeProvider>; |
42 | <MuiThemeProvider theme={theme}> | 43 | }, |
43 | {children} | 44 | ); |
44 | </MuiThemeProvider> | ||
45 | ); | ||
46 | }); | ||
diff --git a/packages/renderer/src/components/ToggleDarkModeButton.tsx b/packages/renderer/src/components/ToggleDarkModeButton.tsx index c8ffdf0..695756a 100644 --- a/packages/renderer/src/components/ToggleDarkModeButton.tsx +++ b/packages/renderer/src/components/ToggleDarkModeButton.tsx | |||
@@ -28,7 +28,9 @@ import { useStore } from './StoreProvider'; | |||
28 | 28 | ||
29 | export default observer(() => { | 29 | export default observer(() => { |
30 | const store = useStore(); | 30 | const store = useStore(); |
31 | const { shared: { shouldUseDarkColors } } = store; | 31 | const { |
32 | shared: { shouldUseDarkColors }, | ||
33 | } = store; | ||
32 | 34 | ||
33 | return ( | 35 | return ( |
34 | <IconButton | 36 | <IconButton |
diff --git a/packages/renderer/src/devTools.ts b/packages/renderer/src/devTools.ts index 3d3ba99..cb695c3 100644 --- a/packages/renderer/src/devTools.ts +++ b/packages/renderer/src/devTools.ts | |||
@@ -35,7 +35,9 @@ import type { IAnyStateTreeNode } from 'mobx-state-tree'; | |||
35 | * @return A promise that resolves when the store was exposed to the devtools. | 35 | * @return A promise that resolves when the store was exposed to the devtools. |
36 | * @see https://github.com/SocketCluster/socketcluster-client/issues/118#issuecomment-469064682 | 36 | * @see https://github.com/SocketCluster/socketcluster-client/issues/118#issuecomment-469064682 |
37 | */ | 37 | */ |
38 | export async function exposeToReduxDevtools(model: IAnyStateTreeNode): Promise<void> { | 38 | export async function exposeToReduxDevtools( |
39 | model: IAnyStateTreeNode, | ||
40 | ): Promise<void> { | ||
39 | (window as { global?: unknown }).global = window; | 41 | (window as { global?: unknown }).global = window; |
40 | 42 | ||
41 | // Hack to load dev dependencies on demand. | 43 | // Hack to load dev dependencies on demand. |
diff --git a/packages/renderer/src/stores/RendererStore.ts b/packages/renderer/src/stores/RendererStore.ts index e684759..0b78ce1 100644 --- a/packages/renderer/src/stores/RendererStore.ts +++ b/packages/renderer/src/stores/RendererStore.ts | |||
@@ -24,12 +24,7 @@ import { | |||
24 | SophieRenderer, | 24 | SophieRenderer, |
25 | ThemeSource, | 25 | ThemeSource, |
26 | } from '@sophie/shared'; | 26 | } from '@sophie/shared'; |
27 | import { | 27 | import { applySnapshot, applyPatch, Instance, types } from 'mobx-state-tree'; |
28 | applySnapshot, | ||
29 | applyPatch, | ||
30 | Instance, | ||
31 | types, | ||
32 | } from 'mobx-state-tree'; | ||
33 | 28 | ||
34 | import { getLogger } from '../utils/log'; | 29 | import { getLogger } from '../utils/log'; |
35 | 30 | ||
@@ -38,29 +33,31 @@ import { getEnv } from './RendererEnv'; | |||
38 | 33 | ||
39 | const log = getLogger('RendererStore'); | 34 | const log = getLogger('RendererStore'); |
40 | 35 | ||
41 | export const rendererStore = types.model('RendererStore', { | 36 | export const rendererStore = types |
42 | shared: types.optional(sharedStore, {}), | 37 | .model('RendererStore', { |
43 | }).actions((self) => ({ | 38 | shared: types.optional(sharedStore, {}), |
44 | setBrowserViewBounds(browserViewBounds: BrowserViewBounds): void { | 39 | }) |
45 | getEnv(self).dispatchMainAction({ | 40 | .actions((self) => ({ |
46 | action: 'set-browser-view-bounds', | 41 | setBrowserViewBounds(browserViewBounds: BrowserViewBounds): void { |
47 | browserViewBounds, | 42 | getEnv(self).dispatchMainAction({ |
48 | }); | 43 | action: 'set-browser-view-bounds', |
49 | }, | 44 | browserViewBounds, |
50 | setThemeSource(themeSource: ThemeSource): void { | 45 | }); |
51 | getEnv(self).dispatchMainAction({ | 46 | }, |
52 | action: 'set-theme-source', | 47 | setThemeSource(themeSource: ThemeSource): void { |
53 | themeSource, | 48 | getEnv(self).dispatchMainAction({ |
54 | }); | 49 | action: 'set-theme-source', |
55 | }, | 50 | themeSource, |
56 | toggleDarkMode(): void { | 51 | }); |
57 | if (self.shared.shouldUseDarkColors) { | 52 | }, |
58 | this.setThemeSource('light'); | 53 | toggleDarkMode(): void { |
59 | } else { | 54 | if (self.shared.shouldUseDarkColors) { |
60 | this.setThemeSource('dark'); | 55 | this.setThemeSource('light'); |
61 | } | 56 | } else { |
62 | }, | 57 | this.setThemeSource('dark'); |
63 | })); | 58 | } |
59 | }, | ||
60 | })); | ||
64 | 61 | ||
65 | export interface RendererStore extends Instance<typeof rendererStore> {} | 62 | export interface RendererStore extends Instance<typeof rendererStore> {} |
66 | 63 | ||
@@ -72,22 +69,26 @@ export interface RendererStore extends Instance<typeof rendererStore> {} | |||
72 | * | 69 | * |
73 | * @param ipc The `sophieRenderer` context bridge. | 70 | * @param ipc The `sophieRenderer` context bridge. |
74 | */ | 71 | */ |
75 | export function createAndConnectRendererStore(ipc: SophieRenderer): RendererStore { | 72 | export function createAndConnectRendererStore( |
73 | ipc: SophieRenderer, | ||
74 | ): RendererStore { | ||
76 | const env: RendererEnv = { | 75 | const env: RendererEnv = { |
77 | dispatchMainAction: ipc.dispatchAction, | 76 | dispatchMainAction: ipc.dispatchAction, |
78 | }; | 77 | }; |
79 | const store = rendererStore.create({}, env); | 78 | const store = rendererStore.create({}, env); |
80 | 79 | ||
81 | ipc.onSharedStoreChange({ | 80 | ipc |
82 | onSnapshot(snapshot) { | 81 | .onSharedStoreChange({ |
83 | applySnapshot(store.shared, snapshot); | 82 | onSnapshot(snapshot) { |
84 | }, | 83 | applySnapshot(store.shared, snapshot); |
85 | onPatch(patch) { | 84 | }, |
86 | applyPatch(store.shared, patch); | 85 | onPatch(patch) { |
87 | }, | 86 | applyPatch(store.shared, patch); |
88 | }).catch((err) => { | 87 | }, |
89 | log.error('Failed to connect to shared store', err); | 88 | }) |
90 | }); | 89 | .catch((err) => { |
90 | log.error('Failed to connect to shared store', err); | ||
91 | }); | ||
91 | 92 | ||
92 | return store; | 93 | return store; |
93 | } | 94 | } |
diff --git a/packages/renderer/tsconfig.json b/packages/renderer/tsconfig.json index 14c3e0c..5453330 100644 --- a/packages/renderer/tsconfig.json +++ b/packages/renderer/tsconfig.json | |||
@@ -3,14 +3,8 @@ | |||
3 | "compilerOptions": { | 3 | "compilerOptions": { |
4 | "noEmit": true, | 4 | "noEmit": true, |
5 | "jsx": "react", | 5 | "jsx": "react", |
6 | "lib": [ | 6 | "lib": ["dom", "dom.iterable", "esnext"], |
7 | "dom", | 7 | "types": ["vite/client"] |
8 | "dom.iterable", | ||
9 | "esnext" | ||
10 | ], | ||
11 | "types": [ | ||
12 | "vite/client" | ||
13 | ] | ||
14 | }, | 8 | }, |
15 | "references": [ | 9 | "references": [ |
16 | { | 10 | { |
diff --git a/packages/renderer/vite.config.js b/packages/renderer/vite.config.js index 6440ead..e20e0f1 100644 --- a/packages/renderer/vite.config.js +++ b/packages/renderer/vite.config.js | |||
@@ -46,9 +46,7 @@ export default { | |||
46 | preserveSymlinks: true, | 46 | preserveSymlinks: true, |
47 | }, | 47 | }, |
48 | optimizeDeps: { | 48 | optimizeDeps: { |
49 | exclude: [ | 49 | exclude: ['@sophie/shared'], |
50 | '@sophie/shared', | ||
51 | ], | ||
52 | }, | 50 | }, |
53 | build: { | 51 | build: { |
54 | target: chrome, | 52 | target: chrome, |
@@ -59,11 +57,7 @@ export default { | |||
59 | minify: !isDevelopment, | 57 | minify: !isDevelopment, |
60 | brotliSize: false, | 58 | brotliSize: false, |
61 | rollupOptions: { | 59 | rollupOptions: { |
62 | external: [ | 60 | external: ['mst-middlewares', 'remotedev', ...builtinModules], |
63 | 'mst-middlewares', | ||
64 | 'remotedev', | ||
65 | ...builtinModules, | ||
66 | ], | ||
67 | output: { | 61 | output: { |
68 | banner, | 62 | banner, |
69 | }, | 63 | }, |
diff --git a/packages/service-inject/.eslintrc.cjs b/packages/service-inject/.eslintrc.cjs index 5555f5b..3131abd 100644 --- a/packages/service-inject/.eslintrc.cjs +++ b/packages/service-inject/.eslintrc.cjs | |||
@@ -5,10 +5,7 @@ module.exports = { | |||
5 | }, | 5 | }, |
6 | overrides: [ | 6 | overrides: [ |
7 | { | 7 | { |
8 | files: [ | 8 | files: ['.eslintrc.cjs', 'esbuild.config.js'], |
9 | '.eslintrc.cjs', | ||
10 | 'esbuild.config.js', | ||
11 | ], | ||
12 | env: { | 9 | env: { |
13 | browser: false, | 10 | browser: false, |
14 | node: true, | 11 | node: true, |
diff --git a/packages/service-inject/esbuild.config.js b/packages/service-inject/esbuild.config.js index d0b04bb..795b0f6 100644 --- a/packages/service-inject/esbuild.config.js +++ b/packages/service-inject/esbuild.config.js | |||
@@ -4,9 +4,7 @@ import getEsbuildConfig from '../../config/getEsbuildConfig.js'; | |||
4 | 4 | ||
5 | export default getEsbuildConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileURLToDirname(import.meta.url), |
7 | entryPoints: [ | 7 | entryPoints: ['src/index.ts'], |
8 | 'src/index.ts', | ||
9 | ], | ||
10 | outfile: 'dist/index.js', | 8 | outfile: 'dist/index.js', |
11 | format: 'iife', | 9 | format: 'iife', |
12 | platform: 'browser', | 10 | platform: 'browser', |
diff --git a/packages/service-inject/tsconfig.json b/packages/service-inject/tsconfig.json index 8f84d98..33ce1de 100644 --- a/packages/service-inject/tsconfig.json +++ b/packages/service-inject/tsconfig.json | |||
@@ -2,20 +2,12 @@ | |||
2 | "extends": "../../config/tsconfig.base.json", | 2 | "extends": "../../config/tsconfig.base.json", |
3 | "compilerOptions": { | 3 | "compilerOptions": { |
4 | "noEmit": true, | 4 | "noEmit": true, |
5 | "lib": [ | 5 | "lib": ["dom", "dom.iterable", "esnext"] |
6 | "dom", | ||
7 | "dom.iterable", | ||
8 | "esnext" | ||
9 | ] | ||
10 | }, | 6 | }, |
11 | "references": [ | 7 | "references": [ |
12 | { | 8 | { |
13 | "path": "../service-shared/tsconfig.build.json" | 9 | "path": "../service-shared/tsconfig.build.json" |
14 | } | 10 | } |
15 | ], | 11 | ], |
16 | "include": [ | 12 | "include": ["src/**/*.ts", ".eslintrc.cjs", "esbuild.config.js"] |
17 | "src/**/*.ts", | ||
18 | ".eslintrc.cjs", | ||
19 | "esbuild.config.js" | ||
20 | ] | ||
21 | } | 13 | } |
diff --git a/packages/service-preload/esbuild.config.js b/packages/service-preload/esbuild.config.js index 66f5e84..d888987 100644 --- a/packages/service-preload/esbuild.config.js +++ b/packages/service-preload/esbuild.config.js | |||
@@ -4,15 +4,11 @@ import getEsbuildConfig from '../../config/getEsbuildConfig.js'; | |||
4 | 4 | ||
5 | export default getEsbuildConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileURLToDirname(import.meta.url), |
7 | entryPoints: [ | 7 | entryPoints: ['src/index.ts'], |
8 | 'src/index.ts', | ||
9 | ], | ||
10 | outfile: 'dist/index.cjs', | 8 | outfile: 'dist/index.cjs', |
11 | format: 'cjs', | 9 | format: 'cjs', |
12 | platform: 'node', | 10 | platform: 'node', |
13 | target: chrome, | 11 | target: chrome, |
14 | sourcemap: 'inline', | 12 | sourcemap: 'inline', |
15 | external: [ | 13 | external: ['electron'], |
16 | 'electron', | ||
17 | ], | ||
18 | }); | 14 | }); |
diff --git a/packages/service-preload/tsconfig.json b/packages/service-preload/tsconfig.json index 768c464..189859e 100644 --- a/packages/service-preload/tsconfig.json +++ b/packages/service-preload/tsconfig.json | |||
@@ -2,11 +2,7 @@ | |||
2 | "extends": "../../config/tsconfig.base.json", | 2 | "extends": "../../config/tsconfig.base.json", |
3 | "compilerOptions": { | 3 | "compilerOptions": { |
4 | "noEmit": true, | 4 | "noEmit": true, |
5 | "lib": [ | 5 | "lib": ["dom", "dom.iterable", "esnext"] |
6 | "dom", | ||
7 | "dom.iterable", | ||
8 | "esnext" | ||
9 | ] | ||
10 | }, | 6 | }, |
11 | "references": [ | 7 | "references": [ |
12 | { | 8 | { |
diff --git a/packages/service-preload/types/importMeta.d.ts b/packages/service-preload/types/importMeta.d.ts index 9b73170..ff3b17c 100644 --- a/packages/service-preload/types/importMeta.d.ts +++ b/packages/service-preload/types/importMeta.d.ts | |||
@@ -3,5 +3,5 @@ interface ImportMeta { | |||
3 | DEV: boolean; | 3 | DEV: boolean; |
4 | MODE: string; | 4 | MODE: string; |
5 | PROD: boolean; | 5 | PROD: boolean; |
6 | } | 6 | }; |
7 | } | 7 | } |
diff --git a/packages/service-shared/.eslintrc.cjs b/packages/service-shared/.eslintrc.cjs index c19829d..25fd252 100644 --- a/packages/service-shared/.eslintrc.cjs +++ b/packages/service-shared/.eslintrc.cjs | |||
@@ -6,10 +6,7 @@ module.exports = { | |||
6 | }, | 6 | }, |
7 | overrides: [ | 7 | overrides: [ |
8 | { | 8 | { |
9 | files: [ | 9 | files: ['.eslintrc.cjs', 'esbuild.config.js'], |
10 | '.eslintrc.cjs', | ||
11 | 'esbuild.config.js', | ||
12 | ], | ||
13 | env: { | 10 | env: { |
14 | node: true, | 11 | node: true, |
15 | }, | 12 | }, |
diff --git a/packages/service-shared/esbuild.config.js b/packages/service-shared/esbuild.config.js index ccee72c..2b0dec8 100644 --- a/packages/service-shared/esbuild.config.js +++ b/packages/service-shared/esbuild.config.js | |||
@@ -4,9 +4,7 @@ import getEsbuildConfig from '../../config/getEsbuildConfig.js'; | |||
4 | 4 | ||
5 | export default getEsbuildConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileURLToDirname(import.meta.url), |
7 | entryPoints: [ | 7 | entryPoints: ['src/index.ts'], |
8 | 'src/index.ts', | ||
9 | ], | ||
10 | outfile: 'dist/index.mjs', | 8 | outfile: 'dist/index.mjs', |
11 | format: 'esm', | 9 | format: 'esm', |
12 | // The package that includes this one will have a header comment, | 10 | // The package that includes this one will have a header comment, |
@@ -14,7 +12,5 @@ export default getEsbuildConfig({ | |||
14 | banner: {}, | 12 | banner: {}, |
15 | platform: 'node', | 13 | platform: 'node', |
16 | target: [chrome, node], | 14 | target: [chrome, node], |
17 | external: [ | 15 | external: ['zod'], |
18 | 'zod', | ||
19 | ], | ||
20 | }); | 16 | }); |
diff --git a/packages/service-shared/src/index.ts b/packages/service-shared/src/index.ts index e111347..94be734 100644 --- a/packages/service-shared/src/index.ts +++ b/packages/service-shared/src/index.ts | |||
@@ -20,11 +20,5 @@ | |||
20 | 20 | ||
21 | export { MainToServiceIpcMessage, ServiceToMainIpcMessage } from './ipc'; | 21 | export { MainToServiceIpcMessage, ServiceToMainIpcMessage } from './ipc'; |
22 | 22 | ||
23 | export type { | 23 | export type { UnreadCount, WebSource } from './schemas'; |
24 | UnreadCount, | 24 | export { unreadCount, webSource } from './schemas'; |
25 | WebSource, | ||
26 | } from './schemas'; | ||
27 | export { | ||
28 | unreadCount, | ||
29 | webSource, | ||
30 | } from './schemas'; | ||
diff --git a/packages/service-shared/src/ipc.ts b/packages/service-shared/src/ipc.ts index c0dab11..e0a8755 100644 --- a/packages/service-shared/src/ipc.ts +++ b/packages/service-shared/src/ipc.ts | |||
@@ -18,8 +18,7 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | export enum MainToServiceIpcMessage { | 21 | export enum MainToServiceIpcMessage {} |
22 | } | ||
23 | 22 | ||
24 | export enum ServiceToMainIpcMessage { | 23 | export enum ServiceToMainIpcMessage { |
25 | ApiExposedInMainWorld = 'sophie-service-to-main:api-exposed-in-main-world', | 24 | ApiExposedInMainWorld = 'sophie-service-to-main:api-exposed-in-main-world', |
diff --git a/packages/service-shared/tsconfig.build.json b/packages/service-shared/tsconfig.build.json index 9a0c835..d300514 100644 --- a/packages/service-shared/tsconfig.build.json +++ b/packages/service-shared/tsconfig.build.json | |||
@@ -6,7 +6,5 @@ | |||
6 | "emitDeclarationOnly": true, | 6 | "emitDeclarationOnly": true, |
7 | "rootDir": "src" | 7 | "rootDir": "src" |
8 | }, | 8 | }, |
9 | "include": [ | 9 | "include": ["src/**/*.ts"] |
10 | "src/**/*.ts" | ||
11 | ] | ||
12 | } | 10 | } |
diff --git a/packages/service-shared/tsconfig.json b/packages/service-shared/tsconfig.json index 3e6c6ff..daad7c4 100644 --- a/packages/service-shared/tsconfig.json +++ b/packages/service-shared/tsconfig.json | |||
@@ -7,9 +7,5 @@ | |||
7 | "noEmit": true, | 7 | "noEmit": true, |
8 | "rootDir": null | 8 | "rootDir": null |
9 | }, | 9 | }, |
10 | "include": [ | 10 | "include": ["src/**/*.ts", ".eslintrc.cjs", "esbuild.config.js"] |
11 | "src/**/*.ts", | ||
12 | ".eslintrc.cjs", | ||
13 | "esbuild.config.js" | ||
14 | ] | ||
15 | } | 11 | } |
diff --git a/packages/shared/.eslintrc.cjs b/packages/shared/.eslintrc.cjs index c19829d..25fd252 100644 --- a/packages/shared/.eslintrc.cjs +++ b/packages/shared/.eslintrc.cjs | |||
@@ -6,10 +6,7 @@ module.exports = { | |||
6 | }, | 6 | }, |
7 | overrides: [ | 7 | overrides: [ |
8 | { | 8 | { |
9 | files: [ | 9 | files: ['.eslintrc.cjs', 'esbuild.config.js'], |
10 | '.eslintrc.cjs', | ||
11 | 'esbuild.config.js', | ||
12 | ], | ||
13 | env: { | 10 | env: { |
14 | node: true, | 11 | node: true, |
15 | }, | 12 | }, |
diff --git a/packages/shared/esbuild.config.js b/packages/shared/esbuild.config.js index 78249ab..44501bd 100644 --- a/packages/shared/esbuild.config.js +++ b/packages/shared/esbuild.config.js | |||
@@ -4,9 +4,7 @@ import getEsbuildConfig from '../../config/getEsbuildConfig.js'; | |||
4 | 4 | ||
5 | export default getEsbuildConfig({ | 5 | export default getEsbuildConfig({ |
6 | absWorkingDir: fileURLToDirname(import.meta.url), | 6 | absWorkingDir: fileURLToDirname(import.meta.url), |
7 | entryPoints: [ | 7 | entryPoints: ['src/index.ts'], |
8 | 'src/index.ts', | ||
9 | ], | ||
10 | outfile: 'dist/index.mjs', | 8 | outfile: 'dist/index.mjs', |
11 | format: 'esm', | 9 | format: 'esm', |
12 | // The package that includes this one will have a header comment, | 10 | // The package that includes this one will have a header comment, |
@@ -14,9 +12,5 @@ export default getEsbuildConfig({ | |||
14 | banner: {}, | 12 | banner: {}, |
15 | platform: 'node', | 13 | platform: 'node', |
16 | target: [chrome, node], | 14 | target: [chrome, node], |
17 | external: [ | 15 | external: ['mobx', 'mobx-state-tree', 'zod'], |
18 | 'mobx', | ||
19 | 'mobx-state-tree', | ||
20 | 'zod', | ||
21 | ], | ||
22 | }); | 16 | }); |
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 9828ec4..6383f63 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts | |||
@@ -22,18 +22,14 @@ export type { SophieRenderer } from './contextBridge/SophieRenderer'; | |||
22 | 22 | ||
23 | export { MainToRendererIpcMessage, RendererToMainIpcMessage } from './ipc'; | 23 | export { MainToRendererIpcMessage, RendererToMainIpcMessage } from './ipc'; |
24 | 24 | ||
25 | export type { | 25 | export type { Action, BrowserViewBounds, ThemeSource } from './schemas'; |
26 | Action, | 26 | export { action, browserViewBounds, themeSource } from './schemas'; |
27 | BrowserViewBounds, | ||
28 | ThemeSource, | ||
29 | } from './schemas'; | ||
30 | export { | ||
31 | action, | ||
32 | browserViewBounds, | ||
33 | themeSource, | ||
34 | } from './schemas'; | ||
35 | 27 | ||
36 | export type { Config, ConfigSnapshotIn, ConfigSnapshotOut } from './stores/Config'; | 28 | export type { |
29 | Config, | ||
30 | ConfigSnapshotIn, | ||
31 | ConfigSnapshotOut, | ||
32 | } from './stores/Config'; | ||
37 | export { config } from './stores/Config'; | 33 | export { config } from './stores/Config'; |
38 | 34 | ||
39 | export type { | 35 | export type { |
diff --git a/packages/shared/src/stores/Config.ts b/packages/shared/src/stores/Config.ts index 432945c..1d98a33 100644 --- a/packages/shared/src/stores/Config.ts +++ b/packages/shared/src/stores/Config.ts | |||
@@ -18,12 +18,7 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { | 21 | import { Instance, types, SnapshotIn, SnapshotOut } from 'mobx-state-tree'; |
22 | Instance, | ||
23 | types, | ||
24 | SnapshotIn, | ||
25 | SnapshotOut, | ||
26 | } from 'mobx-state-tree'; | ||
27 | 22 | ||
28 | import { themeSource } from '../schemas'; | 23 | import { themeSource } from '../schemas'; |
29 | 24 | ||
diff --git a/packages/shared/src/stores/SharedStore.ts b/packages/shared/src/stores/SharedStore.ts index c6c3ddc..cb14394 100644 --- a/packages/shared/src/stores/SharedStore.ts +++ b/packages/shared/src/stores/SharedStore.ts | |||
@@ -37,7 +37,8 @@ export interface SharedStore extends Instance<typeof sharedStore> {} | |||
37 | 37 | ||
38 | export interface SharedStoreSnapshotIn extends SnapshotIn<typeof sharedStore> {} | 38 | export interface SharedStoreSnapshotIn extends SnapshotIn<typeof sharedStore> {} |
39 | 39 | ||
40 | export interface SharedStoreSnapshotOut extends SnapshotOut<typeof sharedStore> {} | 40 | export interface SharedStoreSnapshotOut |
41 | extends SnapshotOut<typeof sharedStore> {} | ||
41 | 42 | ||
42 | export interface SharedStoreListener { | 43 | export interface SharedStoreListener { |
43 | onSnapshot(snapshot: SharedStoreSnapshotIn): void; | 44 | onSnapshot(snapshot: SharedStoreSnapshotIn): void; |
diff --git a/packages/shared/tsconfig.build.json b/packages/shared/tsconfig.build.json index 9a0c835..d300514 100644 --- a/packages/shared/tsconfig.build.json +++ b/packages/shared/tsconfig.build.json | |||
@@ -6,7 +6,5 @@ | |||
6 | "emitDeclarationOnly": true, | 6 | "emitDeclarationOnly": true, |
7 | "rootDir": "src" | 7 | "rootDir": "src" |
8 | }, | 8 | }, |
9 | "include": [ | 9 | "include": ["src/**/*.ts"] |
10 | "src/**/*.ts" | ||
11 | ] | ||
12 | } | 10 | } |
diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json index 3e6c6ff..daad7c4 100644 --- a/packages/shared/tsconfig.json +++ b/packages/shared/tsconfig.json | |||
@@ -7,9 +7,5 @@ | |||
7 | "noEmit": true, | 7 | "noEmit": true, |
8 | "rootDir": null | 8 | "rootDir": null |
9 | }, | 9 | }, |
10 | "include": [ | 10 | "include": ["src/**/*.ts", ".eslintrc.cjs", "esbuild.config.js"] |
11 | "src/**/*.ts", | ||
12 | ".eslintrc.cjs", | ||
13 | "esbuild.config.js" | ||
14 | ] | ||
15 | } | 11 | } |
diff --git a/prettier.config.cjs b/prettier.config.cjs new file mode 100644 index 0000000..de2f53c --- /dev/null +++ b/prettier.config.cjs | |||
@@ -0,0 +1,4 @@ | |||
1 | module.exports = { | ||
2 | singleQuote: true, | ||
3 | trailingComma: 'all', | ||
4 | }; | ||
diff --git a/scripts/build.js b/scripts/build.js index 1236a6c..88267e5 100644 --- a/scripts/build.js +++ b/scripts/build.js | |||
@@ -14,7 +14,9 @@ const thisDir = fileURLToDirname(import.meta.url); | |||
14 | async function buildPackageEsbuild(packageName) { | 14 | async function buildPackageEsbuild(packageName) { |
15 | /** @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. | 16 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Read untyped config file. |
17 | const { default: config } = await import(`../packages/${packageName}/esbuild.config.js`); | 17 | const { default: config } = await import( |
18 | `../packages/${packageName}/esbuild.config.js` | ||
19 | ); | ||
18 | return esbuildBuild(config); | 20 | return esbuildBuild(config); |
19 | } | 21 | } |
20 | 22 | ||
@@ -32,18 +34,21 @@ function buildAll() { | |||
32 | const buildServiceShared = buildPackageEsbuild('service-shared'); | 34 | const buildServiceShared = buildPackageEsbuild('service-shared'); |
33 | const buildShared = buildPackageEsbuild('shared'); | 35 | const buildShared = buildPackageEsbuild('shared'); |
34 | return Promise.all([ | 36 | return Promise.all([ |
35 | Promise.all([ | 37 | Promise.all([buildServiceShared, buildShared]).then(() => |
36 | buildServiceShared, | 38 | buildPackageEsbuild('main'), |
37 | buildShared, | 39 | ), |
38 | ]).then(() => buildPackageEsbuild('main')), | 40 | buildServiceShared.then(() => |
39 | buildServiceShared.then(() => Promise.all([ | 41 | Promise.all([ |
40 | buildPackageEsbuild('service-inject'), | 42 | buildPackageEsbuild('service-inject'), |
41 | buildPackageEsbuild('service-preload'), | 43 | buildPackageEsbuild('service-preload'), |
42 | ])), | 44 | ]), |
43 | buildShared.then(() => Promise.all([ | 45 | ), |
44 | buildPackageEsbuild('preload'), | 46 | buildShared.then(() => |
45 | buildPackageVite('renderer'), | 47 | Promise.all([ |
46 | ])), | 48 | buildPackageEsbuild('preload'), |
49 | buildPackageVite('renderer'), | ||
50 | ]), | ||
51 | ), | ||
47 | ]); | 52 | ]); |
48 | } | 53 | } |
49 | 54 | ||
diff --git a/scripts/update-electron-vendors.js b/scripts/update-electron-vendors.js index 91cdb61..6ff5c06 100644 --- a/scripts/update-electron-vendors.js +++ b/scripts/update-electron-vendors.js | |||
@@ -16,10 +16,13 @@ const thisDir = fileURLToDirname(import.meta.url); | |||
16 | * @returns {NodeJS.ProcessVersions} | 16 | * @returns {NodeJS.ProcessVersions} |
17 | */ | 17 | */ |
18 | function getVendors() { | 18 | function getVendors() { |
19 | const output = execSync(`${electronPath.toString()} -p "JSON.stringify(process.versions)"`, { | 19 | const output = execSync( |
20 | env: { ELECTRON_RUN_AS_NODE: '1' }, | 20 | `${electronPath.toString()} -p "JSON.stringify(process.versions)"`, |
21 | encoding: 'utf-8', | 21 | { |
22 | }); | 22 | env: { ELECTRON_RUN_AS_NODE: '1' }, |
23 | encoding: 'utf-8', | ||
24 | }, | ||
25 | ); | ||
23 | 26 | ||
24 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return -- Read untyped output. | 27 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return -- Read untyped output. |
25 | return JSON.parse(output); | 28 | return JSON.parse(output); |
@@ -34,17 +37,22 @@ function updateVendors() { | |||
34 | const electronRelease = getVendors(); | 37 | const electronRelease = getVendors(); |
35 | 38 | ||
36 | const nodeMajorVersion = electronRelease.node.split('.')[0]; | 39 | const nodeMajorVersion = electronRelease.node.split('.')[0]; |
37 | const chromeMajorVersion = electronRelease.v8.split('.')[0] + electronRelease.v8.split('.')[1]; | 40 | const chromeMajorVersion = |
41 | electronRelease.v8.split('.')[0] + electronRelease.v8.split('.')[1]; | ||
38 | 42 | ||
39 | const browserslistrcPath = join(thisDir, '../.browserslistrc'); | 43 | const browserslistrcPath = join(thisDir, '../.browserslistrc'); |
40 | 44 | ||
41 | return Promise.all([ | 45 | return Promise.all([ |
42 | writeFile( | 46 | writeFile( |
43 | join(thisDir, '../.electron-vendors.cache.json'), | 47 | join(thisDir, '../.electron-vendors.cache.json'), |
44 | `${JSON.stringify({ | 48 | `${JSON.stringify( |
45 | chrome: chromeMajorVersion, | 49 | { |
46 | node: nodeMajorVersion, | 50 | chrome: chromeMajorVersion, |
47 | }, null, 2)}\n`, | 51 | node: nodeMajorVersion, |
52 | }, | ||
53 | null, | ||
54 | 2, | ||
55 | )}\n`, | ||
48 | ), | 56 | ), |
49 | 57 | ||
50 | writeFile(browserslistrcPath, `Chrome ${chromeMajorVersion}\n`, 'utf8'), | 58 | writeFile(browserslistrcPath, `Chrome ${chromeMajorVersion}\n`, 'utf8'), |
diff --git a/scripts/watch.js b/scripts/watch.js index 1345a0f..cf5dbfd 100644 --- a/scripts/watch.js +++ b/scripts/watch.js | |||
@@ -15,7 +15,10 @@ const thisDir = fileURLToDirname(import.meta.url); | |||
15 | const sharedModule = join(thisDir, '../packages/shared/dist/index.mjs'); | 15 | const sharedModule = join(thisDir, '../packages/shared/dist/index.mjs'); |
16 | 16 | ||
17 | /** @type {string} */ | 17 | /** @type {string} */ |
18 | const serviceSharedModule = join(thisDir, '../packages/service-shared/dist/index.mjs'); | 18 | const serviceSharedModule = join( |
19 | thisDir, | ||
20 | '../packages/service-shared/dist/index.mjs', | ||
21 | ); | ||
19 | 22 | ||
20 | /** @type {RegExp[]} */ | 23 | /** @type {RegExp[]} */ |
21 | const stderrIgnorePatterns = [ | 24 | const stderrIgnorePatterns = [ |
@@ -37,7 +40,9 @@ const stderrIgnorePatterns = [ | |||
37 | async function setupEsbuildWatcher(packageName, extraPaths, callback) { | 40 | async function setupEsbuildWatcher(packageName, extraPaths, callback) { |
38 | /** @type {{ default: import('esbuild').BuildOptions }} */ | 41 | /** @type {{ default: import('esbuild').BuildOptions }} */ |
39 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Read untyped config file. | 42 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Read untyped config file. |
40 | const { default: config } = await import(`../packages/${packageName}/esbuild.config.js`); | 43 | const { default: config } = await import( |
44 | `../packages/${packageName}/esbuild.config.js` | ||
45 | ); | ||
41 | config.logLevel = 'info'; | 46 | config.logLevel = 'info'; |
42 | config.incremental = true; | 47 | config.incremental = true; |
43 | 48 | ||
@@ -55,34 +60,37 @@ async function setupEsbuildWatcher(packageName, extraPaths, callback) { | |||
55 | callback(); | 60 | callback(); |
56 | } | 61 | } |
57 | watcher.on('change', () => { | 62 | watcher.on('change', () => { |
58 | incrementalBuild.rebuild?.().then(() => { | 63 | incrementalBuild |
59 | if (callback) { | 64 | .rebuild?.() |
60 | console.log(`\u26a1 Reloading package ${packageName}`); | 65 | .then(() => { |
61 | callback(); | 66 | if (callback) { |
62 | } | 67 | console.log(`\u26a1 Reloading package ${packageName}`); |
63 | }).catch((err) => { | 68 | callback(); |
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 | } | 69 | } |
78 | } | 70 | }) |
79 | console.error( | 71 | .catch((err) => { |
80 | '\ud83d\udd25', | 72 | if (typeof err === 'object' && 'errors' in err) { |
81 | 'error while rebuilding package', | 73 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- We just checked. |
82 | packageName, | 74 | const { errors } = err; |
83 | err, | 75 | if (Array.isArray(errors)) { |
84 | ); | 76 | const errCount = errors.length; |
85 | }); | 77 | console.error( |
78 | '\ud83d\udd25', | ||
79 | errCount, | ||
80 | errCount > 1 ? 'errors' : 'error', | ||
81 | 'while rebuilding package', | ||
82 | packageName, | ||
83 | ); | ||
84 | return; | ||
85 | } | ||
86 | } | ||
87 | console.error( | ||
88 | '\ud83d\udd25', | ||
89 | 'error while rebuilding package', | ||
90 | packageName, | ||
91 | err, | ||
92 | ); | ||
93 | }); | ||
86 | }); | 94 | }); |
87 | } | 95 | } |
88 | 96 | ||
@@ -136,7 +144,11 @@ function setupServicePackageWatcher(packageName, sendEvent) { | |||
136 | */ | 144 | */ |
137 | function setupMainPackageWatcher(viteDevServer) { | 145 | function setupMainPackageWatcher(viteDevServer) { |
138 | // Write a value to an environment variable to pass it to the main process. | 146 | // Write a value to an environment variable to pass it to the main process. |
139 | const { config: { server: { port, https, host } } } = viteDevServer; | 147 | const { |
148 | config: { | ||
149 | server: { port, https, host }, | ||
150 | }, | ||
151 | } = viteDevServer; | ||
140 | const protocol = `http${https ? 's' : ''}:`; | 152 | const protocol = `http${https ? 's' : ''}:`; |
141 | const hostOrDefault = typeof host === 'string' ? host : 'localhost'; | 153 | const hostOrDefault = typeof host === 'string' ? host : 'localhost'; |
142 | const portOrDefault = port || 3000; | 154 | const portOrDefault = port || 3000; |
@@ -148,10 +160,7 @@ function setupMainPackageWatcher(viteDevServer) { | |||
148 | 160 | ||
149 | return setupEsbuildWatcher( | 161 | return setupEsbuildWatcher( |
150 | 'main', | 162 | 'main', |
151 | [ | 163 | [serviceSharedModule, sharedModule], |
152 | serviceSharedModule, | ||
153 | sharedModule, | ||
154 | ], | ||
155 | () => { | 164 | () => { |
156 | if (spawnProcess !== null) { | 165 | if (spawnProcess !== null) { |
157 | spawnProcess.kill('SIGINT'); | 166 | spawnProcess.kill('SIGINT'); |
@@ -191,16 +200,20 @@ async function setupDevEnvironment() { | |||
191 | const sharedWatcher = setupEsbuildWatcher('shared'); | 200 | const sharedWatcher = setupEsbuildWatcher('shared'); |
192 | const serviceSharedWatcher = setupEsbuildWatcher('service-shared'); | 201 | const serviceSharedWatcher = setupEsbuildWatcher('service-shared'); |
193 | await Promise.all([ | 202 | await Promise.all([ |
194 | sharedWatcher.then(() => Promise.all([ | 203 | sharedWatcher.then(() => |
195 | setupPreloadPackageWatcher(sendEvent), | 204 | Promise.all([ |
196 | setupDevServer('renderer').then((devServer) => { | 205 | setupPreloadPackageWatcher(sendEvent), |
197 | viteDevServer = devServer; | 206 | setupDevServer('renderer').then((devServer) => { |
198 | }), | 207 | viteDevServer = devServer; |
199 | ])), | 208 | }), |
200 | serviceSharedWatcher.then(() => Promise.all([ | 209 | ]), |
201 | setupServicePackageWatcher('service-inject', sendEvent), | 210 | ), |
202 | setupServicePackageWatcher('service-preload', sendEvent), | 211 | serviceSharedWatcher.then(() => |
203 | ])), | 212 | Promise.all([ |
213 | setupServicePackageWatcher('service-inject', sendEvent), | ||
214 | setupServicePackageWatcher('service-preload', sendEvent), | ||
215 | ]), | ||
216 | ), | ||
204 | ]); | 217 | ]); |
205 | 218 | ||
206 | if (viteDevServer === null) { | 219 | if (viteDevServer === null) { |
diff --git a/tsconfig.json b/tsconfig.json index 627bed2..4ed25b9 100644 --- a/tsconfig.json +++ b/tsconfig.json | |||
@@ -9,6 +9,7 @@ | |||
9 | "scripts/**/*.js", | 9 | "scripts/**/*.js", |
10 | ".electron-builder.config.cjs", | 10 | ".electron-builder.config.cjs", |
11 | ".eslintrc.cjs", | 11 | ".eslintrc.cjs", |
12 | "jest.config.js" | 12 | "jest.config.js", |
13 | "prettier.config.cjs" | ||
13 | ] | 14 | ] |
14 | } | 15 | } |
@@ -1569,7 +1569,7 @@ __metadata: | |||
1569 | languageName: node | 1569 | languageName: node |
1570 | linkType: hard | 1570 | linkType: hard |
1571 | 1571 | ||
1572 | "@types/prettier@npm:^2.1.5": | 1572 | "@types/prettier@npm:^2, @types/prettier@npm:^2.1.5": |
1573 | version: 2.4.2 | 1573 | version: 2.4.2 |
1574 | resolution: "@types/prettier@npm:2.4.2" | 1574 | resolution: "@types/prettier@npm:2.4.2" |
1575 | checksum: 76e230b2d11028af11fe12e09b2d5b10b03738e9abf819ae6ebb0f78cac13d39f860755ce05ac3855b608222518d956628f5d00322dc206cc6d1f2d8d1519f1e | 1575 | checksum: 76e230b2d11028af11fe12e09b2d5b10b03738e9abf819ae6ebb0f78cac13d39f860755ce05ac3855b608222518d956628f5d00322dc206cc6d1f2d8d1519f1e |
@@ -3998,6 +3998,17 @@ __metadata: | |||
3998 | languageName: node | 3998 | languageName: node |
3999 | linkType: hard | 3999 | linkType: hard |
4000 | 4000 | ||
4001 | "eslint-config-prettier@npm:^8.3.0": | ||
4002 | version: 8.3.0 | ||
4003 | resolution: "eslint-config-prettier@npm:8.3.0" | ||
4004 | peerDependencies: | ||
4005 | eslint: ">=7.0.0" | ||
4006 | bin: | ||
4007 | eslint-config-prettier: bin/cli.js | ||
4008 | checksum: df4cea3032671995bb5ab07e016169072f7fa59f44a53251664d9ca60951b66cdc872683b5c6a3729c91497c11490ca44a79654b395dd6756beb0c3903a37196 | ||
4009 | languageName: node | ||
4010 | linkType: hard | ||
4011 | |||
4001 | "eslint-formatter-gitlab@npm:^3.0.0": | 4012 | "eslint-formatter-gitlab@npm:^3.0.0": |
4002 | version: 3.0.0 | 4013 | version: 3.0.0 |
4003 | resolution: "eslint-formatter-gitlab@npm:3.0.0" | 4014 | resolution: "eslint-formatter-gitlab@npm:3.0.0" |
@@ -4091,6 +4102,21 @@ __metadata: | |||
4091 | languageName: node | 4102 | languageName: node |
4092 | linkType: hard | 4103 | linkType: hard |
4093 | 4104 | ||
4105 | "eslint-plugin-prettier@npm:^4.0.0": | ||
4106 | version: 4.0.0 | ||
4107 | resolution: "eslint-plugin-prettier@npm:4.0.0" | ||
4108 | dependencies: | ||
4109 | prettier-linter-helpers: ^1.0.0 | ||
4110 | peerDependencies: | ||
4111 | eslint: ">=7.28.0" | ||
4112 | prettier: ">=2.0.0" | ||
4113 | peerDependenciesMeta: | ||
4114 | eslint-config-prettier: | ||
4115 | optional: true | ||
4116 | checksum: 03d69177a3c21fa2229c7e427ce604429f0b20ab7f411e2e824912f572a207c7f5a41fd1f0a95b9b8afe121e291c1b1f1dc1d44c7aad4b0837487f9c19f5210d | ||
4117 | languageName: node | ||
4118 | linkType: hard | ||
4119 | |||
4094 | "eslint-plugin-react-hooks@npm:^4.3.0": | 4120 | "eslint-plugin-react-hooks@npm:^4.3.0": |
4095 | version: 4.3.0 | 4121 | version: 4.3.0 |
4096 | resolution: "eslint-plugin-react-hooks@npm:4.3.0" | 4122 | resolution: "eslint-plugin-react-hooks@npm:4.3.0" |
@@ -4348,6 +4374,13 @@ __metadata: | |||
4348 | languageName: node | 4374 | languageName: node |
4349 | linkType: hard | 4375 | linkType: hard |
4350 | 4376 | ||
4377 | "fast-diff@npm:^1.1.2": | ||
4378 | version: 1.2.0 | ||
4379 | resolution: "fast-diff@npm:1.2.0" | ||
4380 | checksum: 1b5306eaa9e826564d9e5ffcd6ebd881eb5f770b3f977fcbf38f05c824e42172b53c79920e8429c54eb742ce15a0caf268b0fdd5b38f6de52234c4a8368131ae | ||
4381 | languageName: node | ||
4382 | linkType: hard | ||
4383 | |||
4351 | "fast-glob@npm:^3.1.1": | 4384 | "fast-glob@npm:^3.1.1": |
4352 | version: 3.2.7 | 4385 | version: 3.2.7 |
4353 | resolution: "fast-glob@npm:3.2.7" | 4386 | resolution: "fast-glob@npm:3.2.7" |
@@ -7214,6 +7247,24 @@ __metadata: | |||
7214 | languageName: node | 7247 | languageName: node |
7215 | linkType: hard | 7248 | linkType: hard |
7216 | 7249 | ||
7250 | "prettier-linter-helpers@npm:^1.0.0": | ||
7251 | version: 1.0.0 | ||
7252 | resolution: "prettier-linter-helpers@npm:1.0.0" | ||
7253 | dependencies: | ||
7254 | fast-diff: ^1.1.2 | ||
7255 | checksum: 00ce8011cf6430158d27f9c92cfea0a7699405633f7f1d4a45f07e21bf78e99895911cbcdc3853db3a824201a7c745bd49bfea8abd5fb9883e765a90f74f8392 | ||
7256 | languageName: node | ||
7257 | linkType: hard | ||
7258 | |||
7259 | "prettier@npm:^2.5.1": | ||
7260 | version: 2.5.1 | ||
7261 | resolution: "prettier@npm:2.5.1" | ||
7262 | bin: | ||
7263 | prettier: bin-prettier.js | ||
7264 | checksum: 21b9408476ea1c544b0e45d51ceb94a84789ff92095abb710942d780c862d0daebdb29972d47f6b4d0f7ebbfb0ffbf56cc2cfa3e3e9d1cca54864af185b15b66 | ||
7265 | languageName: node | ||
7266 | linkType: hard | ||
7267 | |||
7217 | "pretty-format@npm:^27.0.0, pretty-format@npm:^27.4.6": | 7268 | "pretty-format@npm:^27.0.0, pretty-format@npm:^27.4.6": |
7218 | version: 27.4.6 | 7269 | version: 27.4.6 |
7219 | resolution: "pretty-format@npm:27.4.6" | 7270 | resolution: "pretty-format@npm:27.4.6" |
@@ -7924,6 +7975,7 @@ __metadata: | |||
7924 | dependencies: | 7975 | dependencies: |
7925 | "@electron/fuses": ^1.5.0 | 7976 | "@electron/fuses": ^1.5.0 |
7926 | "@types/jest": ^27.4.0 | 7977 | "@types/jest": ^27.4.0 |
7978 | "@types/prettier": ^2 | ||
7927 | "@typescript-eslint/eslint-plugin": ^5.9.0 | 7979 | "@typescript-eslint/eslint-plugin": ^5.9.0 |
7928 | "@typescript-eslint/parser": ^5.9.0 | 7980 | "@typescript-eslint/parser": ^5.9.0 |
7929 | "@vitejs/plugin-react": ^1.1.4 | 7981 | "@vitejs/plugin-react": ^1.1.4 |
@@ -7936,15 +7988,18 @@ __metadata: | |||
7936 | eslint-config-airbnb: ^19.0.4 | 7988 | eslint-config-airbnb: ^19.0.4 |
7937 | eslint-config-airbnb-base: ^15.0.0 | 7989 | eslint-config-airbnb-base: ^15.0.0 |
7938 | eslint-config-airbnb-typescript: ^16.1.0 | 7990 | eslint-config-airbnb-typescript: ^16.1.0 |
7991 | eslint-config-prettier: ^8.3.0 | ||
7939 | eslint-formatter-gitlab: ^3.0.0 | 7992 | eslint-formatter-gitlab: ^3.0.0 |
7940 | eslint-import-resolver-typescript: ^2.5.0 | 7993 | eslint-import-resolver-typescript: ^2.5.0 |
7941 | eslint-plugin-import: ^2.25.4 | 7994 | eslint-plugin-import: ^2.25.4 |
7942 | eslint-plugin-jsx-a11y: ^6.5.1 | 7995 | eslint-plugin-jsx-a11y: ^6.5.1 |
7996 | eslint-plugin-prettier: ^4.0.0 | ||
7943 | eslint-plugin-react: ^7.28.0 | 7997 | eslint-plugin-react: ^7.28.0 |
7944 | eslint-plugin-react-hooks: ^4.3.0 | 7998 | eslint-plugin-react-hooks: ^4.3.0 |
7945 | git-repo-info: ^2.1.1 | 7999 | git-repo-info: ^2.1.1 |
7946 | jest: ^27.4.7 | 8000 | jest: ^27.4.7 |
7947 | preload: ^0.1.0 | 8001 | preload: ^0.1.0 |
8002 | prettier: ^2.5.1 | ||
7948 | rimraf: ^3.0.2 | 8003 | rimraf: ^3.0.2 |
7949 | typescript: ^4.5.4 | 8004 | typescript: ^4.5.4 |
7950 | vite: ^2.7.10 | 8005 | vite: ^2.7.10 |