Skip to content

Commit

Permalink
chore: overhaul project setup (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
achou11 authored Oct 28, 2024
1 parent f3b2c53 commit d6f3037
Show file tree
Hide file tree
Showing 76 changed files with 17,710 additions and 16,505 deletions.
2 changes: 0 additions & 2 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
VITE_ROOT_KEY=
VITE_MAPBOX_ACCESS_TOKEN=
30 changes: 30 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: CI

on:
push:
branches: [main]
pull_request:
# By default, a workflow only runs when a pull_request's activity type is
# opened, synchronize, or reopened. Adding ready_for_review here ensures
# that CI runs when a PR is marked as not a draft, since we skip CI when a
# PR is draft
types: [opened, synchronize, reopened, ready_for_review]

jobs:
all:
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
- name: Install deps
run: npm ci
- name: Build translations
run: npm run intl:translations
- name: Run tests
run: npm test
40 changes: 3 additions & 37 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
Expand Down Expand Up @@ -34,10 +31,6 @@ build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# TypeScript cache
*.tsbuildinfo
Expand All @@ -54,40 +47,10 @@ typings/
# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache

# next.js build output
.next

# nuxt.js build output
.nuxt

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# Webpack
.webpack/

# Vite
.vite/

# Electron-Forge
out/

Expand All @@ -99,3 +62,6 @@ translations/

# Ignore vscode settings (for now)
.vscode/

# Built renderer-code during Forge packaging process
dist/
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20.14
20.18
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/out
/data
/.vite
/messages
/translations
50 changes: 33 additions & 17 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
// @ts-check

/** @type {import("@ianvs/prettier-plugin-sort-imports").PrettierConfig} */
/** @type {import('prettier').Config} */
export default {
semi: false,
singleQuote: true,
arrowParens: 'always',
plugins: ['@ianvs/prettier-plugin-sort-imports'],
// Mostly inspired by examples from https://github.com/IanVS/prettier-plugin-sort-imports?tab=readme-ov-file#importorder
importOrder: [
'<BUILT_IN_MODULES>',
'^react$',
'<THIRD_PARTY_MODULES>',
'',
'^(?!.*[.]css$)[./].*$',
'',
'.css$',
],
tabWidth: 2,
useTabs: true,
semi: false,
singleQuote: true,
arrowParens: 'always',
trailingComma: 'all',
plugins: [
'@ianvs/prettier-plugin-sort-imports',
'./node_modules/prettier-plugin-jsdoc/dist/index.js',
],
/**
* Configuration @ianvs/prettier-plugin-sort-imports
*/
// Mostly inspired by examples from https://github.com/IanVS/prettier-plugin-sort-imports?tab=readme-ov-file#importorder
importOrder: [
'<BUILT_IN_MODULES>',
'^react$',
'<THIRD_PARTY_MODULES>',
'',
'^(?!.*[.]css$)[./].*$',
'',
'.css$',
],
importOrderTypeScriptVersion: '5.6.3',
// TODO: Uncomment when a release newer than 4.3.1 is out
// importOrderCaseSensitive: true,
/**
* Configuration for prettier-plugin-jsdoc
*/
jsdocCommentLineStrategy: 'keep',
jsdocAddDefaultToDescription: false,
jsdocSeparateReturnsFromParam: true,
}
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
node 20.14
node 20.18
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# CoMapeo Desktop

Scaffolded using https://www.electronforge.io/templates/vite.
Go to the [Development](./docs/DEVELOPMENT.md) docs to learn about how the project is structured and how to work on it locally.

Go to the [Development](./docs/DEVELOPMENT.md) docs to learn about how the project's structure and how to work on it locally.
## Quick Start

1. Clone the repo:

```sh
git clone https://github.com/digidem/comapeo-desktop.git
```

2. Install dependencies:

```sh
npm install
```

3. Start the application in development mode:

```sh
npm start
```

## License

[GPL-3.0](./LICENSE)
72 changes: 29 additions & 43 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
- [Processes](#processes)
- [Electron Runtime](#electron-runtime)
- [Developing locally](#developing-locally)
- [Editor setup](#editor-setup)
- [Helpful tips about workflow](#helpful-tips-about-workflow)
- [Helpful tips about configuration](#helpful-tips-about-configuration)
- [Translations](#translations)
Expand All @@ -19,13 +18,15 @@

The following directories can be found in `src/` and approximately represent the Electron processes of interest:

- `main/`: Code that runs Electron's [main process](https://www.electronjs.org/docs/latest/tutorial/process-model#the-main-process)
- [`main/`](../src/main/): Code that runs Electron's [main process](https://www.electronjs.org/docs/latest/tutorial/process-model#the-main-process).
- has access to Node
- `services/`: Code that is spawned and coordinated by Electron's main process, usually as a [`utilityProcess`](https://www.electronjs.org/docs/latest/api/utility-process).
- has direct access to Electron APIs
- [`services/`](../src/services): Code that is spawned and coordinated by Electron's main process, usually as a [`utilityProcess`](https://www.electronjs.org/docs/latest/api/utility-process).
- has access to Node
- `preload/`: Code that is injected as [preload scripts](https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts) into the renderer windows created by the main process
- does not have direct access to Electron APIs.
- [`preload/`](../src/preload/): Code that is injected as [preload scripts](https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts) into the renderer windows created by the main process.
- has access to browser APIs and [some Electron and Node APIs](https://www.electronjs.org/docs/latest/tutorial/sandbox#preload-scripts)
- `renderer/`: Code that runs in Electron's [renderer process](https://www.electronjs.org/docs/latest/tutorial/process-model#the-renderer-process)
- [`renderer/`](../src/renderer/): Code that runs in Electron's [renderer process](https://www.electronjs.org/docs/latest/tutorial/process-model#the-renderer-process).
- has access to browser APIs

### Electron Runtime
Expand All @@ -36,26 +37,15 @@ The following use cases should generally be defined and set up in a preload scri
- any usage of Electron's renderer modules
- usage of a Node API (either directly or through a module) that's supported by the preload script sandbox

For example, in [`src/preload/main-window.ts`](../src/preload/main-window.ts), we expose an API to interact with the main process on `window.runtime`. This may change in the future but it's actually quite nice because it becomes more accessible for debugging purposes (e.g. testing the api using the devtools console)
For example, in [`src/preload/main-window.js`](../src/preload/main-window.js), we expose an API to interact with the main process on `window.runtime`. This may change in the future but it's actually quite nice because it becomes more accessible for debugging purposes (e.g. testing the API using the devtools console).

## Developing locally

Make sure you have the desired Node version installed. For this project we encourage using the version that's specified in [`.nvmrc`](../.nvmrc) (or [`.tool-versions`](../.tool-versions)) file. We recommend using a proper version management tool to install and manage Node versions, such as [nvm](https://github.com/nvm-sh/nvm), [fnm](https://github.com/Schniz/fnm), [asdf](https://asdf-vm.com/), or [mise](https://mise.jdx.dev/).
Make sure you have the desired Node version installed. For this project we encourage using the version that's specified in the [`.nvmrc`](../.nvmrc) (or [`.tool-versions`](../.tool-versions)) file. We recommend using a proper version management tool to install and manage Node versions, such as [nvm](https://github.com/nvm-sh/nvm), [fnm](https://github.com/Schniz/fnm), [asdf](https://asdf-vm.com/), or [mise](https://mise.jdx.dev/).

### Environment variables

Create a copy of the [`.env.template`](../.env.template) and call it `.env`.

- `VITE_ROOT_KEY`: This is a hacky and insufficient workaround for storing an encryption key (see https://github.com/digidem/comapeo-desktop/issues/25). In order to generate a valid value, you can run the following Node code (e.g. in a REPL or as a script) and copy the value that's logged (omit any quotation marks when inserting it into the env file):

```js
const crypto = require('node:crypto')
console.log(crypto.randomBytes(16).toString('hex'))
```

_**This will eventually be removed from this file.**_

- `VITE_MAPBOX_ACCESS_TOKEN`: A public access token from Mapbox ([documentation](https://docs.mapbox.com/help/getting-started/access-tokens/)) that allows the app to access online map styles. Reach out to the team or create your own.
Create a copy of the [`.env.template`](../.env.template) and call it `.env`. At the moment, we do not make use of this, but this will most likely change soon.

### Running the app

Expand All @@ -66,31 +56,23 @@ npm install # Install dependencies
npm start # Build translations, then build the app in development mode and start the development server
```

### Editor setup

- VSCode: if you're using the [ESLint VSCode plugin](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint), you should add the following to your settings so that the ESLint configuration is detected properly:

```json
{
"eslint.experimental.useFlatConfig": true
}
```

### Helpful tips about workflow

- Changes in the `renderer/` should immediately automatically be reflected in the app
- Changes in the `preload/` require the window to be refreshed to be reflected in the relevant window. Either go to the `View > Reload` menu option or use the keyboard shortcut (e.g. <kbd>CMD + R</kbd> on macOS, <kbd>CTRL + R</kbd> on Linux, Windows)
- Changes to `main/` and `services/` require restarting the app. Stop the process that is running `npm start` and rerun it
- Changes in the `src/renderer/` should immediately automatically be reflected in the app
- Changes in the `src/preload/` require the window to be refreshed to be reflected in the relevant window. Either go to the `View > Reload` menu option or use the keyboard shortcut (e.g. <kbd>CMD + R</kbd> on macOS, <kbd>CTRL + R</kbd> on Linux, Windows)
- Changes to `src/main/` require restarting the app. You can either:
1. Stop the process that is running `npm start` and rerun it.
2. Type <kbd>R + S + Enter</kbd> in the process that is running `npm start`, which tells Forge to restart the main process.
- In development, the `userData` directory is set to the `data/` directory by default. This provides the following benefits:
- Avoids conflict with the existing app if it's installed. Assuming the same app id is used, Electron will default to using the OS-specific user data directory, which means that if you have a production version of the app installed, starting the development version will read and write from the production user data directory. Most of the time this is not desired (you generally don't want to mix production data and settings with your development environment). If it is desired, comment out the line that calls `app.setPath('userData', ...)` in [`src/main/index.ts`](../src/main/index.ts)
- Avoids conflicting with the existing app if it's installed. Assuming the same app id is used, Electron will default to using the OS-specific user data directory, which means that if you have a production version of the app installed, starting the development version will read and write from the production user data directory. Most of the time this is not desired (you generally don't want to mix production data and settings with your development environment). If it is desired, comment out the line that calls `app.setPath('userData', ...)` in [`src/main/index.js`](../src/main/index.js)
- Easier to debug because you don't have to spend as much time to figure out which directory to look at (it changes based on operating system)
- If you want to change the `userData` directory, define an environment variable called `USER_DATA_PATH` that can be used when calling `npm start`. For example, running `USER_DATA_PATH=./my_data npm start` will create a `my_data` directory relative to the project root. This is useful for creating different "profiles" and isolating data for the purpose of testing features or reproducing bugs
- **If you are installing a package that is only going to be used by code the renderer (e.g. a React component library), you most likely should install it as a dev dependency instead of a direct dependency**. This differs from typical development workflows you see elsewhere, but the reasoning is that during the packaging stage of the app, [`@electron/packager`](https://github.com/electron/packager) avoids copying dev dependencies found in the `node_modules` directory. Since we bundle our renderer code, we do not need to copy over these dependencies, which results in a significant decrease in disk space occupied by the app.

### Helpful tips about configuration

- The most helpful starting point is at [`forge.config.js`](/forge.config.js). This defines the configuration used by Electron Forge and its Vite plugin. If you need to make updates to how various JS bundles are created, find the appropriate Vite config mapping in that file and update the config of interest.
- The TypeScript setup is a little clunky right now. The main challenge is that each of the processes have different runtime environments. There doesn't seem to be a straightfoward way to configure the project to be aware of all of the nuances of Electron's processes, so instead we define a TypeScript configuration for each process and stick a tsconfig file in each one, which is then used by the appropriate Vite configuration.
- _NOTE: there's probably a much better way of setting this up - Andrew welcomes any input and changes to it_
- The configuration for the renderer app is defined in the [Vite configuration file](../src/renderer/vite.config.js) that lives in the `src/renderer`.
- The configuration for the Electron app is located in [`forge.config.js`](/forge.config.js).

## Translations

Expand All @@ -102,22 +84,26 @@ In order to update translations, run `npm run intl:translations`, which will ext

- For the main process:

1. Use `defineMessage` (or `defineMessages`) from `@formatjs/intl` to create messages and use in the main process code
2. Run npm run `intl:translations:main` (or`npm run intl:translations`)
1. Use `defineMessage` (or `defineMessages`) from `@formatjs/intl` to create messages and use in the main process code.
2. Run npm run `intl:translations:main` (or`npm run intl:translations`).

- For the renderer process:

1. Use `defineMessage` (or `defineMessages`) from `react-intl` to create messages and use in the renderer process code
2. Run npm run `intl:translations:renderer` (or`npm run intl:translations`)
1. Use `defineMessage` (or `defineMessages`) from `react-intl` to create messages and use in the renderer process code.
2. Run npm run `intl:translations:renderer` (or`npm run intl:translations`).

## Building the app

The [Electron Forge docs](https://www.electronforge.io/) are pretty informative (especially https://www.electronforge.io/core-concepts/build-lifecycle) but in a nutshell:

- `npm run package`: generate an executable app bundle i.e. an executable that you can run in the commandline
- `npm run forge:package`: generate an executable app bundle i.e. an executable that you can run in the command-line.

- `npm run make`: generate an distributable installer or archives that you can install by opening using your filesystem
- `npm run forge:make`: generate an distributable installer or archives that you can install by opening using your filesystem.

- `npm run publish`: upload the distributable to some cloud storage for distribution
- `npm run forge:publish`: upload the distributable to some cloud storage for distribution.

All commands place the built assets in the `out/` directory.

If you're running into an error with any of the Forge-related commands but not seeing any output in the console, you probably have to prefix the command with `DEBUG=electron-forge` e.g. `DEBUG=electron-forge npm run forge:package`.

By default, we package the app in the [ASAR](https://github.com/electron/asar) format. However, it can be helpful to avoid doing that for debugging purposes (e.g. building locally), in which case you can specify a `ASAR=true` env variable when running the relevant Forge command e.g. `ASAR=true npm run forge:package`.
Loading

0 comments on commit d6f3037

Please sign in to comment.