mirror of
https://github.com/CringeStudios/element-desktop.git
synced 2025-01-18 23:44:59 +01:00
Format all files with prettier
This commit is contained in:
parent
040344eeab
commit
0faac52dae
14
.eslintrc.js
14
.eslintrc.js
@ -1,8 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: ["matrix-org"],
|
plugins: ["matrix-org"],
|
||||||
extends: [
|
extends: ["plugin:matrix-org/javascript"],
|
||||||
"plugin:matrix-org/javascript",
|
|
||||||
],
|
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 2021,
|
ecmaVersion: 2021,
|
||||||
},
|
},
|
||||||
@ -20,11 +18,10 @@ module.exports = {
|
|||||||
"prefer-promise-reject-errors": "off",
|
"prefer-promise-reject-errors": "off",
|
||||||
"no-async-promise-executor": "off",
|
"no-async-promise-executor": "off",
|
||||||
},
|
},
|
||||||
overrides: [{
|
overrides: [
|
||||||
|
{
|
||||||
files: ["{src,scripts,hak}/**/*.{ts,tsx}"],
|
files: ["{src,scripts,hak}/**/*.{ts,tsx}"],
|
||||||
extends: [
|
extends: ["plugin:matrix-org/typescript"],
|
||||||
"plugin:matrix-org/typescript",
|
|
||||||
],
|
|
||||||
rules: {
|
rules: {
|
||||||
// Things we do that break the ideal style
|
// Things we do that break the ideal style
|
||||||
"prefer-promise-reject-errors": "off",
|
"prefer-promise-reject-errors": "off",
|
||||||
@ -34,5 +31,6 @@ module.exports = {
|
|||||||
// We're okay with assertion errors when we ask for them
|
// We're okay with assertion errors when we ask for them
|
||||||
"@typescript-eslint/no-non-null-assertion": "off",
|
"@typescript-eslint/no-non-null-assertion": "off",
|
||||||
},
|
},
|
||||||
}],
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
## Checklist
|
## Checklist
|
||||||
|
|
||||||
* [ ] Ensure your code works with manual testing
|
- [ ] Ensure your code works with manual testing
|
||||||
* [ ] Linter and other CI checks pass
|
- [ ] Linter and other CI checks pass
|
||||||
* [ ] Sign-off given on the changes (see [CONTRIBUTING.md](https://github.com/vector-im/element-desktop/blob/develop/CONTRIBUTING.md))
|
- [ ] Sign-off given on the changes (see [CONTRIBUTING.md](https://github.com/vector-im/element-desktop/blob/develop/CONTRIBUTING.md))
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
If you would like to specify text for the changelog entry other than your PR title, add the following:
|
If you would like to specify text for the changelog entry other than your PR title, add the following:
|
||||||
|
4
.github/renovate.json
vendored
4
.github/renovate.json
vendored
@ -1,6 +1,4 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": [
|
"extends": ["github>matrix-org/renovate-config-element-web"]
|
||||||
"github>matrix-org/renovate-config-element-web"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
24
.github/workflows/build.yaml
vendored
24
.github/workflows/build.yaml
vendored
@ -1,8 +1,8 @@
|
|||||||
name: Build and Test
|
name: Build and Test
|
||||||
on:
|
on:
|
||||||
pull_request: { }
|
pull_request: {}
|
||||||
push:
|
push:
|
||||||
branches: [ develop, master ]
|
branches: [develop, master]
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
@ -19,7 +19,7 @@ jobs:
|
|||||||
uses: ./.github/workflows/build_windows.yaml
|
uses: ./.github/workflows/build_windows.yaml
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
arch: [ x64, x86 ]
|
arch: [x64, x86]
|
||||||
with:
|
with:
|
||||||
arch: ${{ matrix.arch }}
|
arch: ${{ matrix.arch }}
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ jobs:
|
|||||||
uses: ./.github/workflows/build_linux.yaml
|
uses: ./.github/workflows/build_linux.yaml
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
sqlcipher: [ system, static ]
|
sqlcipher: [system, static]
|
||||||
with:
|
with:
|
||||||
sqlcipher: ${{ matrix.sqlcipher }}
|
sqlcipher: ${{ matrix.sqlcipher }}
|
||||||
|
|
||||||
@ -46,18 +46,18 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# Disable macOS tests for now, they fail to run in CI, needs investigation.
|
# Disable macOS tests for now, they fail to run in CI, needs investigation.
|
||||||
# - name: macOS Universal
|
# - name: macOS Universal
|
||||||
# os: macos
|
# os: macos
|
||||||
# artifact: macos
|
# artifact: macos
|
||||||
# executable: "./dist/mac-universal/Element.app/Contents/MacOS/Element"
|
# executable: "./dist/mac-universal/Element.app/Contents/MacOS/Element"
|
||||||
# prepare_cmd: "chmod +x ./dist/mac-universal/Element.app/Contents/MacOS/Element"
|
# prepare_cmd: "chmod +x ./dist/mac-universal/Element.app/Contents/MacOS/Element"
|
||||||
- name: 'Linux (sqlcipher: system)'
|
- name: "Linux (sqlcipher: system)"
|
||||||
os: ubuntu
|
os: ubuntu
|
||||||
artifact: linux-sqlcipher-system
|
artifact: linux-sqlcipher-system
|
||||||
executable: "element-desktop"
|
executable: "element-desktop"
|
||||||
prepare_cmd: "sudo apt install ./dist/*.deb"
|
prepare_cmd: "sudo apt install ./dist/*.deb"
|
||||||
- name: 'Linux (sqlcipher: static)'
|
- name: "Linux (sqlcipher: static)"
|
||||||
os: ubuntu
|
os: ubuntu
|
||||||
artifact: linux-sqlcipher-static
|
artifact: linux-sqlcipher-static
|
||||||
executable: "element-desktop"
|
executable: "element-desktop"
|
||||||
|
10
.github/workflows/packages_index.yaml
vendored
10
.github/workflows/packages_index.yaml
vendored
@ -2,21 +2,21 @@ name: Generate packages.element.io directory indexes
|
|||||||
on:
|
on:
|
||||||
# Trigger a rebuild of all indexes if the template gets updated
|
# Trigger a rebuild of all indexes if the template gets updated
|
||||||
push:
|
push:
|
||||||
branches: [ develop ]
|
branches: [develop]
|
||||||
paths:
|
paths:
|
||||||
- 'packages.element.io/**'
|
- "packages.element.io/**"
|
||||||
# Trigger a daily rebuild for nightlies
|
# Trigger a daily rebuild for nightlies
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 11 * * *'
|
- cron: "0 11 * * *"
|
||||||
# Manual trigger for rebuilding for releases
|
# Manual trigger for rebuilding for releases
|
||||||
workflow_dispatch: { }
|
workflow_dispatch: {}
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
name: "Deploy"
|
name: "Deploy"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
environment: develop
|
environment: develop
|
||||||
env:
|
env:
|
||||||
R2_BUCKET: 'packages-element-io'
|
R2_BUCKET: "packages-element-io"
|
||||||
R2_URL: ${{ secrets.CF_R2_S3_API }}
|
R2_URL: ${{ secrets.CF_R2_S3_API }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
2
.github/workflows/pull_request.yaml
vendored
2
.github/workflows/pull_request.yaml
vendored
@ -1,7 +1,7 @@
|
|||||||
name: Pull Request
|
name: Pull Request
|
||||||
on:
|
on:
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [ opened, edited, labeled, unlabeled, synchronize ]
|
types: [opened, edited, labeled, unlabeled, synchronize]
|
||||||
concurrency: ${{ github.workflow }}-${{ github.event.pull_request.head.ref }}
|
concurrency: ${{ github.workflow }}-${{ github.event.pull_request.head.ref }}
|
||||||
jobs:
|
jobs:
|
||||||
action:
|
action:
|
||||||
|
8
.github/workflows/static_analysis.yaml
vendored
8
.github/workflows/static_analysis.yaml
vendored
@ -1,8 +1,8 @@
|
|||||||
name: Static Analysis
|
name: Static Analysis
|
||||||
on:
|
on:
|
||||||
pull_request: { }
|
pull_request: {}
|
||||||
push:
|
push:
|
||||||
branches: [ develop, master ]
|
branches: [develop, master]
|
||||||
jobs:
|
jobs:
|
||||||
ts_lint:
|
ts_lint:
|
||||||
name: "Typescript Syntax Check"
|
name: "Typescript Syntax Check"
|
||||||
@ -12,7 +12,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
cache: 'yarn'
|
cache: "yarn"
|
||||||
|
|
||||||
# Does not need branch matching as only analyses this layer
|
# Does not need branch matching as only analyses this layer
|
||||||
- name: Install Deps
|
- name: Install Deps
|
||||||
@ -33,7 +33,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
cache: 'yarn'
|
cache: "yarn"
|
||||||
|
|
||||||
# Does not need branch matching as only analyses this layer
|
# Does not need branch matching as only analyses this layer
|
||||||
- name: Install Deps
|
- name: Install Deps
|
||||||
|
2
.github/workflows/upgrade_dependencies.yml
vendored
2
.github/workflows/upgrade_dependencies.yml
vendored
@ -1,6 +1,6 @@
|
|||||||
name: Upgrade Dependencies
|
name: Upgrade Dependencies
|
||||||
on:
|
on:
|
||||||
workflow_dispatch: { }
|
workflow_dispatch: {}
|
||||||
jobs:
|
jobs:
|
||||||
upgrade:
|
upgrade:
|
||||||
uses: matrix-org/matrix-js-sdk/.github/workflows/upgrade_dependencies.yml@develop
|
uses: matrix-org/matrix-js-sdk/.github/workflows/upgrade_dependencies.yml@develop
|
||||||
|
19
.prettierignore
Normal file
19
.prettierignore
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/build/
|
||||||
|
/dockerbuild/
|
||||||
|
/lib/
|
||||||
|
/node_modules/
|
||||||
|
/packages.elememt.io/
|
||||||
|
/webapp
|
||||||
|
/src/i18n/strings
|
||||||
|
/CHANGELOG.md
|
||||||
|
/package-lock.json
|
||||||
|
/yarn.lock
|
||||||
|
|
||||||
|
**/.idea
|
||||||
|
.vscode
|
||||||
|
.vscode/
|
||||||
|
.tmp
|
||||||
|
.env
|
||||||
|
/coverage
|
||||||
|
/.npmrc
|
||||||
|
/*.log
|
1
.prettierrc.js
Normal file
1
.prettierrc.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require("eslint-plugin-matrix-org/.prettierrc.js");
|
54
README.md
54
README.md
@ -5,21 +5,20 @@
|
|||||||
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=element-desktop&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=element-desktop)
|
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=element-desktop&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=element-desktop)
|
||||||
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=element-desktop&metric=bugs)](https://sonarcloud.io/summary/new_code?id=element-desktop)
|
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=element-desktop&metric=bugs)](https://sonarcloud.io/summary/new_code?id=element-desktop)
|
||||||
|
|
||||||
Element Desktop
|
# Element Desktop
|
||||||
===============
|
|
||||||
|
|
||||||
Element Desktop is a Matrix client for desktop platforms with Element Web at its core.
|
Element Desktop is a Matrix client for desktop platforms with Element Web at its core.
|
||||||
|
|
||||||
First Steps
|
# First Steps
|
||||||
===========
|
|
||||||
Before you do anything else, fetch the dependencies:
|
Before you do anything else, fetch the dependencies:
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn install
|
yarn install
|
||||||
```
|
```
|
||||||
|
|
||||||
Fetching Element
|
# Fetching Element
|
||||||
================
|
|
||||||
Since this package is just the Electron wrapper for Element Web, it doesn't contain any of the Element Web code,
|
Since this package is just the Electron wrapper for Element Web, it doesn't contain any of the Element Web code,
|
||||||
so the first step is to get a working copy of Element Web. There are a few ways of doing this:
|
so the first step is to get a working copy of Element Web. There are a few ways of doing this:
|
||||||
|
|
||||||
@ -31,6 +30,7 @@ yarn run fetch --noverify --cfgdir ""
|
|||||||
```
|
```
|
||||||
|
|
||||||
...or if you'd like to use GPG to verify the downloaded package:
|
...or if you'd like to use GPG to verify the downloaded package:
|
||||||
|
|
||||||
```
|
```
|
||||||
# Fetch the Element public key from the element.io web server over a secure connection and import
|
# Fetch the Element public key from the element.io web server over a secure connection and import
|
||||||
# it into your local GPG keychain (you'll need GPG installed). You only need to to do this
|
# it into your local GPG keychain (you'll need GPG installed). You only need to to do this
|
||||||
@ -41,6 +41,7 @@ yarn run fetch --cfgdir ""
|
|||||||
```
|
```
|
||||||
|
|
||||||
...or either of the above, but fetching a specific version of Element:
|
...or either of the above, but fetching a specific version of Element:
|
||||||
|
|
||||||
```
|
```
|
||||||
# Fetch the prebuilt release Element package from the element-web GitHub releases page. The version
|
# Fetch the prebuilt release Element package from the element-web GitHub releases page. The version
|
||||||
# fetched will be the same as the local element-desktop package.
|
# fetched will be the same as the local element-desktop package.
|
||||||
@ -49,6 +50,7 @@ yarn run fetch --noverify --cfgdir "" v1.5.6
|
|||||||
|
|
||||||
If you only want to run the app locally and don't need to build packages, you can
|
If you only want to run the app locally and don't need to build packages, you can
|
||||||
provide the `webapp` directory directly:
|
provide the `webapp` directory directly:
|
||||||
|
|
||||||
```
|
```
|
||||||
# Assuming you've checked out and built a copy of element-web in ../element-web
|
# Assuming you've checked out and built a copy of element-web in ../element-web
|
||||||
ln -s ../element-web/webapp ./
|
ln -s ../element-web/webapp ./
|
||||||
@ -56,8 +58,7 @@ ln -s ../element-web/webapp ./
|
|||||||
|
|
||||||
[TODO: add support for fetching develop builds, arbitrary URLs and arbitrary paths]
|
[TODO: add support for fetching develop builds, arbitrary URLs and arbitrary paths]
|
||||||
|
|
||||||
Building
|
# Building
|
||||||
========
|
|
||||||
|
|
||||||
## Native Build
|
## Native Build
|
||||||
|
|
||||||
@ -67,18 +68,22 @@ Optionally, [build the native modules](https://github.com/vector-im/element-desk
|
|||||||
which include support for searching in encrypted rooms and secure storage. Skipping this step is fine, you just won't have those features.
|
which include support for searching in encrypted rooms and secure storage. Skipping this step is fine, you just won't have those features.
|
||||||
|
|
||||||
Then, run
|
Then, run
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn run build
|
yarn run build
|
||||||
```
|
```
|
||||||
|
|
||||||
This will do a couple of things:
|
This will do a couple of things:
|
||||||
* Run the `setversion` script to set the local package version to match whatever
|
|
||||||
|
- Run the `setversion` script to set the local package version to match whatever
|
||||||
version of Element you installed above.
|
version of Element you installed above.
|
||||||
* Run electron-builder to build a package. The package built will match the operating system
|
- Run electron-builder to build a package. The package built will match the operating system
|
||||||
you're running the build process on.
|
you're running the build process on.
|
||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
|
|
||||||
Alternatively, you can also build using docker, which will always produce the linux package:
|
Alternatively, you can also build using docker, which will always produce the linux package:
|
||||||
|
|
||||||
```
|
```
|
||||||
# Run this once to make the docker image
|
# Run this once to make the docker image
|
||||||
yarn run docker:setup
|
yarn run docker:setup
|
||||||
@ -91,9 +96,10 @@ yarn run docker:build
|
|||||||
|
|
||||||
After running, the packages should be in `dist/`.
|
After running, the packages should be in `dist/`.
|
||||||
|
|
||||||
Starting
|
# Starting
|
||||||
========
|
|
||||||
If you'd just like to run the electron app locally for development:
|
If you'd just like to run the electron app locally for development:
|
||||||
|
|
||||||
```
|
```
|
||||||
# Install electron - we don't normally need electron itself as it's provided
|
# Install electron - we don't normally need electron itself as it's provided
|
||||||
# by electron-builder when building packages
|
# by electron-builder when building packages
|
||||||
@ -101,21 +107,22 @@ yarn add electron
|
|||||||
yarn start
|
yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
Config
|
# Config
|
||||||
======
|
|
||||||
If you'd like the packaged Element to have a configuration file, you can create a
|
If you'd like the packaged Element to have a configuration file, you can create a
|
||||||
config directory and place `config.json` in there, then specify this directory
|
config directory and place `config.json` in there, then specify this directory
|
||||||
with the `--cfgdir` option to `yarn run fetch`, eg:
|
with the `--cfgdir` option to `yarn run fetch`, eg:
|
||||||
|
|
||||||
```
|
```
|
||||||
mkdir myconfig
|
mkdir myconfig
|
||||||
cp /path/to/my/config.json myconfig/
|
cp /path/to/my/config.json myconfig/
|
||||||
yarn run fetch --cfgdir myconfig
|
yarn run fetch --cfgdir myconfig
|
||||||
```
|
```
|
||||||
|
|
||||||
The config dir for the official Element app is in `element.io`. If you use this,
|
The config dir for the official Element app is in `element.io`. If you use this,
|
||||||
your app will auto-update itself using builds from element.io.
|
your app will auto-update itself using builds from element.io.
|
||||||
|
|
||||||
Profiles
|
# Profiles
|
||||||
========
|
|
||||||
|
|
||||||
To run multiple instances of the desktop app for different accounts, you can
|
To run multiple instances of the desktop app for different accounts, you can
|
||||||
launch the executable with the `--profile` argument followed by a unique
|
launch the executable with the `--profile` argument followed by a unique
|
||||||
@ -125,20 +132,18 @@ not interfere with the default one.
|
|||||||
Alternatively, a custom location for the profile data can be specified using the
|
Alternatively, a custom location for the profile data can be specified using the
|
||||||
`--profile-dir` flag followed by the desired path.
|
`--profile-dir` flag followed by the desired path.
|
||||||
|
|
||||||
User-specified config.json
|
# User-specified config.json
|
||||||
==========================
|
|
||||||
|
|
||||||
+ `%APPDATA%\$NAME\config.json` on Windows
|
- `%APPDATA%\$NAME\config.json` on Windows
|
||||||
+ `$XDG_CONFIG_HOME/$NAME/config.json` or `~/.config/$NAME/config.json` on Linux
|
- `$XDG_CONFIG_HOME/$NAME/config.json` or `~/.config/$NAME/config.json` on Linux
|
||||||
+ `~/Library/Application Support/$NAME/config.json` on macOS
|
- `~/Library/Application Support/$NAME/config.json` on macOS
|
||||||
|
|
||||||
In the paths above, `$NAME` is typically `Element`, unless you use `--profile
|
In the paths above, `$NAME` is typically `Element`, unless you use `--profile
|
||||||
$PROFILE` in which case it becomes `Element-$PROFILE`, or it is using one of
|
$PROFILE` in which case it becomes `Element-$PROFILE`, or it is using one of
|
||||||
the above created by a pre-1.7 install, in which case it will be `Riot` or
|
the above created by a pre-1.7 install, in which case it will be `Riot` or
|
||||||
`Riot-$PROFILE`.
|
`Riot-$PROFILE`.
|
||||||
|
|
||||||
Translations
|
# Translations
|
||||||
==========================
|
|
||||||
|
|
||||||
To add a new translation, head to the [translating doc](https://github.com/vector-im/element-web/blob/develop/docs/translating.md).
|
To add a new translation, head to the [translating doc](https://github.com/vector-im/element-web/blob/develop/docs/translating.md).
|
||||||
|
|
||||||
@ -146,8 +151,7 @@ For a developer guide, see the [translating dev doc](https://github.com/vector-i
|
|||||||
|
|
||||||
[<img src="https://translate.element.io/widgets/element-desktop/-/multi-auto.svg" alt="translationsstatus" width="340">](https://translate.element.io/engage/element-desktop/?utm_source=widget)
|
[<img src="https://translate.element.io/widgets/element-desktop/-/multi-auto.svg" alt="translationsstatus" width="340">](https://translate.element.io/engage/element-desktop/?utm_source=widget)
|
||||||
|
|
||||||
Report bugs & give feedback
|
# Report bugs & give feedback
|
||||||
==========================
|
|
||||||
|
|
||||||
If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.
|
If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.
|
||||||
|
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [
|
presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-typescript"],
|
||||||
['@babel/preset-env', { targets: { node: 'current' } }],
|
|
||||||
'@babel/preset-typescript',
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
@ -17,13 +17,14 @@ when releasing.
|
|||||||
|
|
||||||
Install the pre-requisites for your system:
|
Install the pre-requisites for your system:
|
||||||
|
|
||||||
* [Windows pre-requisites](https://github.com/vector-im/element-desktop/blob/develop/docs/windows-requirements.md)
|
- [Windows pre-requisites](https://github.com/vector-im/element-desktop/blob/develop/docs/windows-requirements.md)
|
||||||
* Linux: TODO
|
- Linux: TODO
|
||||||
* OS X: TODO
|
- OS X: TODO
|
||||||
|
|
||||||
Then optionally, [add seshat and dependencies to support search in E2E rooms](#adding-seshat-for-search-in-e2e-encrypted-rooms).
|
Then optionally, [add seshat and dependencies to support search in E2E rooms](#adding-seshat-for-search-in-e2e-encrypted-rooms).
|
||||||
|
|
||||||
Then, to build for an architecture selected automatically based on your system (recommended), run:
|
Then, to build for an architecture selected automatically based on your system (recommended), run:
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn run build:native
|
yarn run build:native
|
||||||
```
|
```
|
||||||
@ -82,15 +83,19 @@ and https://github.com/vector-im/element-web/issues/20926.
|
|||||||
### macOS
|
### macOS
|
||||||
|
|
||||||
On macOS, you can build universal native modules too:
|
On macOS, you can build universal native modules too:
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn run build:native:universal
|
yarn run build:native:universal
|
||||||
```
|
```
|
||||||
|
|
||||||
...or you can build for a specific architecture:
|
...or you can build for a specific architecture:
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn run build:native --target x86_64-apple-darwin
|
yarn run build:native --target x86_64-apple-darwin
|
||||||
```
|
```
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn run build:native --target aarch64-apple-darwin
|
yarn run build:native --target aarch64-apple-darwin
|
||||||
```
|
```
|
||||||
@ -105,10 +110,13 @@ yarn run build:universal
|
|||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
If you're on Windows, you can choose to build specifically for 32 or 64 bit:
|
If you're on Windows, you can choose to build specifically for 32 or 64 bit:
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn run build:32
|
yarn run build:32
|
||||||
```
|
```
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn run build:64
|
yarn run build:64
|
||||||
```
|
```
|
||||||
|
@ -13,4 +13,3 @@ and can be served by any compatible Squirrel server, such as https://github.com/
|
|||||||
|
|
||||||
On macOS the update mechanism used is [Squirrel.Mac](https://github.com/Squirrel/Squirrel.Mac)
|
On macOS the update mechanism used is [Squirrel.Mac](https://github.com/Squirrel/Squirrel.Mac)
|
||||||
using the newer JSON format as documented [here](https://github.com/Squirrel/Squirrel.Mac#update-file-json-format).
|
using the newer JSON format as documented [here](https://github.com/Squirrel/Squirrel.Mac#update-file-json-format).
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ If you want to build native modules, make sure that the following tools are inst
|
|||||||
Once installed make sure all those utilities are accessible in your `PATH`.
|
Once installed make sure all those utilities are accessible in your `PATH`.
|
||||||
|
|
||||||
If you want to be able to build x86 targets from an x64 host install the right toolchain:
|
If you want to be able to build x86 targets from an x64 host install the right toolchain:
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
rustup toolchain install stable-i686-pc-windows-msvc
|
rustup toolchain install stable-i686-pc-windows-msvc
|
||||||
rustup target add i686-pc-windows-msvc
|
rustup target add i686-pc-windows-msvc
|
||||||
|
@ -16,11 +16,7 @@
|
|||||||
"uisi_autorageshake_app": "element-auto-uisi",
|
"uisi_autorageshake_app": "element-auto-uisi",
|
||||||
"showLabsSettings": true,
|
"showLabsSettings": true,
|
||||||
"roomDirectory": {
|
"roomDirectory": {
|
||||||
"servers": [
|
"servers": ["matrix.org", "gitter.im", "libera.chat"]
|
||||||
"matrix.org",
|
|
||||||
"gitter.im",
|
|
||||||
"libera.chat"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"enable_presence_by_hs_url": {
|
"enable_presence_by_hs_url": {
|
||||||
"https://matrix.org": false,
|
"https://matrix.org": false,
|
||||||
|
@ -15,11 +15,7 @@
|
|||||||
"bug_report_endpoint_url": "https://element.io/bugreports/submit",
|
"bug_report_endpoint_url": "https://element.io/bugreports/submit",
|
||||||
"uisi_autorageshake_app": "element-auto-uisi",
|
"uisi_autorageshake_app": "element-auto-uisi",
|
||||||
"roomDirectory": {
|
"roomDirectory": {
|
||||||
"servers": [
|
"servers": ["matrix.org", "gitter.im", "libera.chat"]
|
||||||
"matrix.org",
|
|
||||||
"gitter.im",
|
|
||||||
"libera.chat"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"showLabsSettings": false,
|
"showLabsSettings": false,
|
||||||
"enable_presence_by_hs_url": {
|
"enable_presence_by_hs_url": {
|
||||||
|
@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
|
|
||||||
import HakEnv from '../../scripts/hak/hakEnv';
|
import HakEnv from "../../scripts/hak/hakEnv";
|
||||||
import { DependencyInfo } from '../../scripts/hak/dep';
|
import { DependencyInfo } from "../../scripts/hak/dep";
|
||||||
|
|
||||||
export default async function buildKeytar(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function buildKeytar(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
const env = hakEnv.makeGypEnv();
|
const env = hakEnv.makeGypEnv();
|
||||||
@ -26,15 +26,15 @@ export default async function buildKeytar(hakEnv: HakEnv, moduleInfo: Dependency
|
|||||||
console.log("Running yarn with env", env);
|
console.log("Running yarn with env", env);
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn(
|
||||||
path.join(moduleInfo.nodeModuleBinDir, 'node-gyp' + (hakEnv.isWin() ? '.cmd' : '')),
|
path.join(moduleInfo.nodeModuleBinDir, "node-gyp" + (hakEnv.isWin() ? ".cmd" : "")),
|
||||||
['rebuild'],
|
["rebuild"],
|
||||||
{
|
{
|
||||||
cwd: moduleInfo.moduleBuildDir,
|
cwd: moduleInfo.moduleBuildDir,
|
||||||
env,
|
env,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
proc.on('exit', (code) => {
|
proc.on("exit", (code) => {
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -14,20 +14,20 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
|
|
||||||
import HakEnv from '../../scripts/hak/hakEnv';
|
import HakEnv from "../../scripts/hak/hakEnv";
|
||||||
import { DependencyInfo } from '../../scripts/hak/dep';
|
import { DependencyInfo } from "../../scripts/hak/dep";
|
||||||
|
|
||||||
export default async function(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
const tools = [['python', '--version']]; // node-gyp uses python for reasons beyond comprehension
|
const tools = [["python", "--version"]]; // node-gyp uses python for reasons beyond comprehension
|
||||||
|
|
||||||
for (const tool of tools) {
|
for (const tool of tools) {
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(tool[0], tool.slice(1), {
|
const proc = childProcess.spawn(tool[0], tool.slice(1), {
|
||||||
stdio: ['ignore'],
|
stdio: ["ignore"],
|
||||||
});
|
});
|
||||||
proc.on('exit', (code) => {
|
proc.on("exit", (code) => {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
reject("Can't find " + tool);
|
reject("Can't find " + tool);
|
||||||
} else {
|
} else {
|
||||||
|
@ -14,15 +14,15 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
import mkdirp from 'mkdirp';
|
import mkdirp from "mkdirp";
|
||||||
import fsExtra from 'fs-extra';
|
import fsExtra from "fs-extra";
|
||||||
|
|
||||||
import HakEnv from '../../scripts/hak/hakEnv';
|
import HakEnv from "../../scripts/hak/hakEnv";
|
||||||
import { DependencyInfo } from '../../scripts/hak/dep';
|
import { DependencyInfo } from "../../scripts/hak/dep";
|
||||||
|
|
||||||
export default async function(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
if (hakEnv.isWin()) {
|
if (hakEnv.isWin()) {
|
||||||
await buildOpenSslWin(hakEnv, moduleInfo);
|
await buildOpenSslWin(hakEnv, moduleInfo);
|
||||||
await buildSqlCipherWin(hakEnv, moduleInfo);
|
await buildSqlCipherWin(hakEnv, moduleInfo);
|
||||||
@ -36,99 +36,91 @@ async function buildOpenSslWin(hakEnv: HakEnv, moduleInfo: DependencyInfo): Prom
|
|||||||
const version = moduleInfo.cfg.dependencies.openssl;
|
const version = moduleInfo.cfg.dependencies.openssl;
|
||||||
const openSslDir = path.join(moduleInfo.moduleTargetDotHakDir, `openssl-${version}`);
|
const openSslDir = path.join(moduleInfo.moduleTargetDotHakDir, `openssl-${version}`);
|
||||||
|
|
||||||
const openSslArch = hakEnv.getTargetArch() === 'x64' ? 'VC-WIN64A' : 'VC-WIN32';
|
const openSslArch = hakEnv.getTargetArch() === "x64" ? "VC-WIN64A" : "VC-WIN32";
|
||||||
|
|
||||||
console.log("Building openssl in " + openSslDir);
|
console.log("Building openssl in " + openSslDir);
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn(
|
||||||
'perl',
|
"perl",
|
||||||
[
|
[
|
||||||
'Configure',
|
"Configure",
|
||||||
'--prefix=' + moduleInfo.depPrefix,
|
"--prefix=" + moduleInfo.depPrefix,
|
||||||
// sqlcipher only uses about a tiny part of openssl. We link statically
|
// sqlcipher only uses about a tiny part of openssl. We link statically
|
||||||
// so will only pull in the symbols we use, but we may as well turn off
|
// so will only pull in the symbols we use, but we may as well turn off
|
||||||
// as much as possible to save on build time.
|
// as much as possible to save on build time.
|
||||||
'no-afalgeng',
|
"no-afalgeng",
|
||||||
'no-capieng',
|
"no-capieng",
|
||||||
'no-cms',
|
"no-cms",
|
||||||
'no-ct',
|
"no-ct",
|
||||||
'no-deprecated',
|
"no-deprecated",
|
||||||
'no-dgram',
|
"no-dgram",
|
||||||
'no-dso',
|
"no-dso",
|
||||||
'no-ec',
|
"no-ec",
|
||||||
'no-ec2m',
|
"no-ec2m",
|
||||||
'no-gost',
|
"no-gost",
|
||||||
'no-nextprotoneg',
|
"no-nextprotoneg",
|
||||||
'no-ocsp',
|
"no-ocsp",
|
||||||
'no-sock',
|
"no-sock",
|
||||||
'no-srp',
|
"no-srp",
|
||||||
'no-srtp',
|
"no-srtp",
|
||||||
'no-tests',
|
"no-tests",
|
||||||
'no-ssl',
|
"no-ssl",
|
||||||
'no-tls',
|
"no-tls",
|
||||||
'no-dtls',
|
"no-dtls",
|
||||||
'no-shared',
|
"no-shared",
|
||||||
'no-aria',
|
"no-aria",
|
||||||
'no-camellia',
|
"no-camellia",
|
||||||
'no-cast',
|
"no-cast",
|
||||||
'no-chacha',
|
"no-chacha",
|
||||||
'no-cmac',
|
"no-cmac",
|
||||||
'no-des',
|
"no-des",
|
||||||
'no-dh',
|
"no-dh",
|
||||||
'no-dsa',
|
"no-dsa",
|
||||||
'no-ecdh',
|
"no-ecdh",
|
||||||
'no-ecdsa',
|
"no-ecdsa",
|
||||||
'no-idea',
|
"no-idea",
|
||||||
'no-md4',
|
"no-md4",
|
||||||
'no-mdc2',
|
"no-mdc2",
|
||||||
'no-ocb',
|
"no-ocb",
|
||||||
'no-poly1305',
|
"no-poly1305",
|
||||||
'no-rc2',
|
"no-rc2",
|
||||||
'no-rc4',
|
"no-rc4",
|
||||||
'no-rmd160',
|
"no-rmd160",
|
||||||
'no-scrypt',
|
"no-scrypt",
|
||||||
'no-seed',
|
"no-seed",
|
||||||
'no-siphash',
|
"no-siphash",
|
||||||
'no-sm2',
|
"no-sm2",
|
||||||
'no-sm3',
|
"no-sm3",
|
||||||
'no-sm4',
|
"no-sm4",
|
||||||
'no-whirlpool',
|
"no-whirlpool",
|
||||||
openSslArch,
|
openSslArch,
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
cwd: openSslDir,
|
cwd: openSslDir,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
proc.on('exit', (code) => {
|
proc.on("exit", (code) => {
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn("nmake", ["build_libs"], {
|
||||||
'nmake',
|
|
||||||
['build_libs'],
|
|
||||||
{
|
|
||||||
cwd: openSslDir,
|
cwd: openSslDir,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
},
|
});
|
||||||
);
|
proc.on("exit", (code) => {
|
||||||
proc.on('exit', (code) => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn("nmake", ["install_dev"], {
|
||||||
'nmake',
|
|
||||||
['install_dev'],
|
|
||||||
{
|
|
||||||
cwd: openSslDir,
|
cwd: openSslDir,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
},
|
});
|
||||||
);
|
proc.on("exit", (code) => {
|
||||||
proc.on('exit', (code) => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -137,38 +129,28 @@ async function buildOpenSslWin(hakEnv: HakEnv, moduleInfo: DependencyInfo): Prom
|
|||||||
async function buildSqlCipherWin(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
async function buildSqlCipherWin(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
const version = moduleInfo.cfg.dependencies.sqlcipher;
|
const version = moduleInfo.cfg.dependencies.sqlcipher;
|
||||||
const sqlCipherDir = path.join(moduleInfo.moduleTargetDotHakDir, `sqlcipher-${version}`);
|
const sqlCipherDir = path.join(moduleInfo.moduleTargetDotHakDir, `sqlcipher-${version}`);
|
||||||
const buildDir = path.join(sqlCipherDir, 'bld');
|
const buildDir = path.join(sqlCipherDir, "bld");
|
||||||
|
|
||||||
await mkdirp(buildDir);
|
await mkdirp(buildDir);
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn("nmake", ["/f", path.join("..", "Makefile.msc"), "libsqlite3.lib", "TOP=.."], {
|
||||||
'nmake',
|
|
||||||
['/f', path.join('..', 'Makefile.msc'), 'libsqlite3.lib', 'TOP=..'],
|
|
||||||
{
|
|
||||||
cwd: buildDir,
|
cwd: buildDir,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
env: Object.assign({}, process.env, {
|
env: Object.assign({}, process.env, {
|
||||||
CCOPTS: "-DSQLITE_HAS_CODEC -I" + path.join(moduleInfo.depPrefix, 'include'),
|
CCOPTS: "-DSQLITE_HAS_CODEC -I" + path.join(moduleInfo.depPrefix, "include"),
|
||||||
LTLIBPATHS: "/LIBPATH:" + path.join(moduleInfo.depPrefix, 'lib'),
|
LTLIBPATHS: "/LIBPATH:" + path.join(moduleInfo.depPrefix, "lib"),
|
||||||
LTLIBS: "libcrypto.lib",
|
LTLIBS: "libcrypto.lib",
|
||||||
}),
|
}),
|
||||||
},
|
});
|
||||||
);
|
proc.on("exit", (code) => {
|
||||||
proc.on('exit', (code) => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await fsExtra.copy(
|
await fsExtra.copy(path.join(buildDir, "libsqlite3.lib"), path.join(moduleInfo.depPrefix, "lib", "sqlcipher.lib"));
|
||||||
path.join(buildDir, 'libsqlite3.lib'),
|
|
||||||
path.join(moduleInfo.depPrefix, 'lib', 'sqlcipher.lib'),
|
|
||||||
);
|
|
||||||
|
|
||||||
await fsExtra.copy(
|
await fsExtra.copy(path.join(buildDir, "sqlite3.h"), path.join(moduleInfo.depPrefix, "include", "sqlcipher.h"));
|
||||||
path.join(buildDir, 'sqlite3.h'),
|
|
||||||
path.join(moduleInfo.depPrefix, 'include', 'sqlcipher.h'),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildSqlCipherUnix(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
async function buildSqlCipherUnix(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
@ -176,21 +158,21 @@ async function buildSqlCipherUnix(hakEnv: HakEnv, moduleInfo: DependencyInfo): P
|
|||||||
const sqlCipherDir = path.join(moduleInfo.moduleTargetDotHakDir, `sqlcipher-${version}`);
|
const sqlCipherDir = path.join(moduleInfo.moduleTargetDotHakDir, `sqlcipher-${version}`);
|
||||||
|
|
||||||
const args = [
|
const args = [
|
||||||
'--prefix=' + moduleInfo.depPrefix + '',
|
"--prefix=" + moduleInfo.depPrefix + "",
|
||||||
'--enable-tempstore=yes',
|
"--enable-tempstore=yes",
|
||||||
'--enable-shared=no',
|
"--enable-shared=no",
|
||||||
'--enable-tcl=no',
|
"--enable-tcl=no",
|
||||||
];
|
];
|
||||||
|
|
||||||
if (hakEnv.isMac()) {
|
if (hakEnv.isMac()) {
|
||||||
args.push('--with-crypto-lib=commoncrypto');
|
args.push("--with-crypto-lib=commoncrypto");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hakEnv.wantsStaticSqlCipherUnix()) {
|
if (hakEnv.wantsStaticSqlCipherUnix()) {
|
||||||
args.push('--enable-tcl=no');
|
args.push("--enable-tcl=no");
|
||||||
|
|
||||||
if (hakEnv.isLinux()) {
|
if (hakEnv.isLinux()) {
|
||||||
args.push('--with-pic=yes');
|
args.push("--with-pic=yes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,9 +183,7 @@ async function buildSqlCipherUnix(hakEnv: HakEnv, moduleInfo: DependencyInfo): P
|
|||||||
args.push(`--host=${hakEnv.getTargetId()}`);
|
args.push(`--host=${hakEnv.getTargetId()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const cflags = [
|
const cflags = ["-DSQLITE_HAS_CODEC"];
|
||||||
'-DSQLITE_HAS_CODEC',
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!hakEnv.isHost()) {
|
if (!hakEnv.isHost()) {
|
||||||
// `clang` uses more logical option naming.
|
// `clang` uses more logical option naming.
|
||||||
@ -211,58 +191,46 @@ async function buildSqlCipherUnix(hakEnv: HakEnv, moduleInfo: DependencyInfo): P
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cflags.length) {
|
if (cflags.length) {
|
||||||
args.push(`CFLAGS=${cflags.join(' ')}`);
|
args.push(`CFLAGS=${cflags.join(" ")}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ldflags: string[] = [];
|
const ldflags: string[] = [];
|
||||||
|
|
||||||
if (hakEnv.isMac()) {
|
if (hakEnv.isMac()) {
|
||||||
ldflags.push('-framework Security');
|
ldflags.push("-framework Security");
|
||||||
ldflags.push('-framework Foundation');
|
ldflags.push("-framework Foundation");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ldflags.length) {
|
if (ldflags.length) {
|
||||||
args.push(`LDFLAGS=${ldflags.join(' ')}`);
|
args.push(`LDFLAGS=${ldflags.join(" ")}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn(path.join(sqlCipherDir, "configure"), args, {
|
||||||
path.join(sqlCipherDir, 'configure'),
|
|
||||||
args,
|
|
||||||
{
|
|
||||||
cwd: sqlCipherDir,
|
cwd: sqlCipherDir,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
},
|
});
|
||||||
);
|
proc.on("exit", (code) => {
|
||||||
proc.on('exit', (code) => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn("make", [], {
|
||||||
'make',
|
|
||||||
[],
|
|
||||||
{
|
|
||||||
cwd: sqlCipherDir,
|
cwd: sqlCipherDir,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
},
|
});
|
||||||
);
|
proc.on("exit", (code) => {
|
||||||
proc.on('exit', (code) => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn("make", ["install"], {
|
||||||
'make',
|
|
||||||
['install'],
|
|
||||||
{
|
|
||||||
cwd: sqlCipherDir,
|
cwd: sqlCipherDir,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
},
|
});
|
||||||
);
|
proc.on("exit", (code) => {
|
||||||
proc.on('exit', (code) => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -277,8 +245,8 @@ async function buildMatrixSeshat(hakEnv: HakEnv, moduleInfo: DependencyInfo): Pr
|
|||||||
if (!hakEnv.isLinux() || hakEnv.wantsStaticSqlCipherUnix()) {
|
if (!hakEnv.isLinux() || hakEnv.wantsStaticSqlCipherUnix()) {
|
||||||
Object.assign(env, {
|
Object.assign(env, {
|
||||||
SQLCIPHER_STATIC: 1,
|
SQLCIPHER_STATIC: 1,
|
||||||
SQLCIPHER_LIB_DIR: path.join(moduleInfo.depPrefix, 'lib'),
|
SQLCIPHER_LIB_DIR: path.join(moduleInfo.depPrefix, "lib"),
|
||||||
SQLCIPHER_INCLUDE_DIR: path.join(moduleInfo.depPrefix, 'include'),
|
SQLCIPHER_INCLUDE_DIR: path.join(moduleInfo.depPrefix, "include"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,11 +266,11 @@ async function buildMatrixSeshat(hakEnv: HakEnv, moduleInfo: DependencyInfo): Pr
|
|||||||
// --exclude-libs ALL
|
// --exclude-libs ALL
|
||||||
// Prevent symbols from being exported by any archive libraries.
|
// Prevent symbols from being exported by any archive libraries.
|
||||||
// Reduces output filesize and prevents being dynamically linked against.
|
// Reduces output filesize and prevents being dynamically linked against.
|
||||||
env.RUSTFLAGS = '-Clink-arg=-Wl,-Bsymbolic -Clink-arg=-Wl,--exclude-libs,ALL';
|
env.RUSTFLAGS = "-Clink-arg=-Wl,-Bsymbolic -Clink-arg=-Wl,--exclude-libs,ALL";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hakEnv.isWin()) {
|
if (hakEnv.isWin()) {
|
||||||
env.RUSTFLAGS = '-Ctarget-feature=+crt-static -Clink-args=libcrypto.lib';
|
env.RUSTFLAGS = "-Ctarget-feature=+crt-static -Clink-args=libcrypto.lib";
|
||||||
// Note that in general, you can specify targets in Rust without having to have
|
// Note that in general, you can specify targets in Rust without having to have
|
||||||
// the matching toolchain, however for this, cargo gets confused when building
|
// the matching toolchain, however for this, cargo gets confused when building
|
||||||
// the build scripts since they run on the host, but vcvarsall.bat sets the c
|
// the build scripts since they run on the host, but vcvarsall.bat sets the c
|
||||||
@ -318,15 +286,15 @@ async function buildMatrixSeshat(hakEnv: HakEnv, moduleInfo: DependencyInfo): Pr
|
|||||||
console.log("Running neon with env", env);
|
console.log("Running neon with env", env);
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn(
|
||||||
path.join(moduleInfo.nodeModuleBinDir, 'neon' + (hakEnv.isWin() ? '.cmd' : '')),
|
path.join(moduleInfo.nodeModuleBinDir, "neon" + (hakEnv.isWin() ? ".cmd" : "")),
|
||||||
['build', '--release'],
|
["build", "--release"],
|
||||||
{
|
{
|
||||||
cwd: moduleInfo.moduleBuildDir,
|
cwd: moduleInfo.moduleBuildDir,
|
||||||
env,
|
env,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
proc.on('exit', (code) => {
|
proc.on("exit", (code) => {
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -14,20 +14,20 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
import fsProm from 'fs/promises';
|
import fsProm from "fs/promises";
|
||||||
|
|
||||||
import HakEnv from '../../scripts/hak/hakEnv';
|
import HakEnv from "../../scripts/hak/hakEnv";
|
||||||
import { DependencyInfo } from '../../scripts/hak/dep';
|
import { DependencyInfo } from "../../scripts/hak/dep";
|
||||||
|
|
||||||
export default async function(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
if (hakEnv.wantsStaticSqlCipher()) {
|
if (hakEnv.wantsStaticSqlCipher()) {
|
||||||
// of course tcl doesn't have a --version
|
// of course tcl doesn't have a --version
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn('tclsh', [], {
|
const proc = childProcess.spawn("tclsh", [], {
|
||||||
stdio: ['pipe', 'ignore', 'ignore'],
|
stdio: ["pipe", "ignore", "ignore"],
|
||||||
});
|
});
|
||||||
proc.on('exit', (code) => {
|
proc.on("exit", (code) => {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
reject("Can't find tclsh - have you installed TCL?");
|
reject("Can't find tclsh - have you installed TCL?");
|
||||||
} else {
|
} else {
|
||||||
@ -39,24 +39,24 @@ export default async function(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promi
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tools = [
|
const tools = [
|
||||||
['rustc', '--version'],
|
["rustc", "--version"],
|
||||||
['python', '--version'], // node-gyp uses python for reasons beyond comprehension
|
["python", "--version"], // node-gyp uses python for reasons beyond comprehension
|
||||||
];
|
];
|
||||||
if (hakEnv.isWin()) {
|
if (hakEnv.isWin()) {
|
||||||
tools.push(['perl', '--version']); // for openssl configure
|
tools.push(["perl", "--version"]); // for openssl configure
|
||||||
tools.push(['nasm', '-v']); // for openssl building
|
tools.push(["nasm", "-v"]); // for openssl building
|
||||||
tools.push(['patch', '--version']); // to patch sqlcipher Makefile.msc
|
tools.push(["patch", "--version"]); // to patch sqlcipher Makefile.msc
|
||||||
tools.push(['nmake', '/?']);
|
tools.push(["nmake", "/?"]);
|
||||||
} else {
|
} else {
|
||||||
tools.push(['make', '--version']);
|
tools.push(["make", "--version"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const tool of tools) {
|
for (const tool of tools) {
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(tool[0], tool.slice(1), {
|
const proc = childProcess.spawn(tool[0], tool.slice(1), {
|
||||||
stdio: ['ignore'],
|
stdio: ["ignore"],
|
||||||
});
|
});
|
||||||
proc.on('exit', (code) => {
|
proc.on("exit", (code) => {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
reject("Can't find " + tool);
|
reject("Can't find " + tool);
|
||||||
} else {
|
} else {
|
||||||
@ -68,19 +68,24 @@ export default async function(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promi
|
|||||||
|
|
||||||
// Ensure Rust target exists (nb. we avoid depending on rustup)
|
// Ensure Rust target exists (nb. we avoid depending on rustup)
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
const rustc = childProcess.execFile('rustc', [
|
const rustc = childProcess.execFile(
|
||||||
'--target', hakEnv.getTargetId(), '-o', 'tmp', '-',
|
"rustc",
|
||||||
], (err, out) => {
|
["--target", hakEnv.getTargetId(), "-o", "tmp", "-"],
|
||||||
|
(err, out) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(
|
reject(
|
||||||
"rustc can't build for target " + hakEnv.getTargetId() +
|
"rustc can't build for target " +
|
||||||
": ensure target is installed via `rustup target add " + hakEnv.getTargetId() + "` " +
|
hakEnv.getTargetId() +
|
||||||
|
": ensure target is installed via `rustup target add " +
|
||||||
|
hakEnv.getTargetId() +
|
||||||
|
"` " +
|
||||||
"or your package manager if not using `rustup`",
|
"or your package manager if not using `rustup`",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
fsProm.unlink('tmp').then(resolve);
|
fsProm.unlink("tmp").then(resolve);
|
||||||
});
|
},
|
||||||
rustc.stdin!.write('fn main() {}');
|
);
|
||||||
|
rustc.stdin!.write("fn main() {}");
|
||||||
rustc.stdin!.end();
|
rustc.stdin!.end();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
import fs from 'fs';
|
import fs from "fs";
|
||||||
import fsProm from 'fs/promises';
|
import fsProm from "fs/promises";
|
||||||
import tar from 'tar';
|
import tar from "tar";
|
||||||
import fetch from 'node-fetch';
|
import fetch from "node-fetch";
|
||||||
import { promises as stream } from "stream";
|
import { promises as stream } from "stream";
|
||||||
|
|
||||||
import HakEnv from '../../scripts/hak/hakEnv';
|
import HakEnv from "../../scripts/hak/hakEnv";
|
||||||
import { DependencyInfo } from '../../scripts/hak/dep';
|
import { DependencyInfo } from "../../scripts/hak/dep";
|
||||||
|
|
||||||
async function download(url: string, filename: string): Promise<void> {
|
async function download(url: string, filename: string): Promise<void> {
|
||||||
const resp = await fetch(url);
|
const resp = await fetch(url);
|
||||||
@ -32,7 +32,7 @@ async function download(url: string, filename: string): Promise<void> {
|
|||||||
await stream.pipeline(resp.body, fs.createWriteStream(filename));
|
await stream.pipeline(resp.body, fs.createWriteStream(filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
if (hakEnv.wantsStaticSqlCipher()) {
|
if (hakEnv.wantsStaticSqlCipher()) {
|
||||||
await getSqlCipher(hakEnv, moduleInfo);
|
await getSqlCipher(hakEnv, moduleInfo);
|
||||||
}
|
}
|
||||||
@ -83,15 +83,11 @@ async function getSqlCipher(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise
|
|||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const readStream = fs.createReadStream(patchFile);
|
const readStream = fs.createReadStream(patchFile);
|
||||||
|
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn("patch", ["-p1"], {
|
||||||
'patch',
|
|
||||||
['-p1'],
|
|
||||||
{
|
|
||||||
cwd: sqlCipherDir,
|
cwd: sqlCipherDir,
|
||||||
stdio: ['pipe', 'inherit', 'inherit'],
|
stdio: ["pipe", "inherit", "inherit"],
|
||||||
},
|
});
|
||||||
);
|
proc.on("exit", (code) => {
|
||||||
proc.on('exit', (code) => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
readStream.pipe(proc.stdin);
|
readStream.pipe(proc.stdin);
|
||||||
|
@ -5,14 +5,9 @@
|
|||||||
"target": "es2016",
|
"target": "es2016",
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"lib": [
|
"lib": ["es2019"]
|
||||||
"es2019",
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["../scripts/@types/*.d.ts", "./**/*.ts"],
|
||||||
"../scripts/@types/*.d.ts",
|
|
||||||
"./**/*.ts"
|
|
||||||
],
|
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
"transpileOnly": true
|
"transpileOnly": true
|
||||||
}
|
}
|
||||||
|
@ -79,8 +79,9 @@
|
|||||||
"electron-devtools-installer": "^3.1.1",
|
"electron-devtools-installer": "^3.1.1",
|
||||||
"eslint": "^8.26.0",
|
"eslint": "^8.26.0",
|
||||||
"eslint-config-google": "^0.14.0",
|
"eslint-config-google": "^0.14.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-import": "^2.25.4",
|
"eslint-plugin-import": "^2.25.4",
|
||||||
"eslint-plugin-matrix-org": "^0.8.0",
|
"eslint-plugin-matrix-org": "^0.9.0",
|
||||||
"eslint-plugin-unicorn": "^45.0.0",
|
"eslint-plugin-unicorn": "^45.0.0",
|
||||||
"expect-playwright": "^0.8.0",
|
"expect-playwright": "^0.8.0",
|
||||||
"find-npm-prefix": "^1.0.2",
|
"find-npm-prefix": "^1.0.2",
|
||||||
|
@ -1,30 +1,27 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-Thin.woff2?v=3.19") format("woff2"),
|
src: url("Inter-Thin.woff2?v=3.19") format("woff2"), url("Inter-Thin.woff?v=3.19") format("woff");
|
||||||
url("Inter-Thin.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-ThinItalic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-ThinItalic.woff2?v=3.19") format("woff2"), url("Inter-ThinItalic.woff?v=3.19") format("woff");
|
||||||
url("Inter-ThinItalic.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 200;
|
font-weight: 200;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-ExtraLight.woff2?v=3.19") format("woff2"),
|
src: url("Inter-ExtraLight.woff2?v=3.19") format("woff2"), url("Inter-ExtraLight.woff?v=3.19") format("woff");
|
||||||
url("Inter-ExtraLight.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 200;
|
font-weight: 200;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
@ -33,66 +30,59 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-Light.woff2?v=3.19") format("woff2"),
|
src: url("Inter-Light.woff2?v=3.19") format("woff2"), url("Inter-Light.woff?v=3.19") format("woff");
|
||||||
url("Inter-Light.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-LightItalic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-LightItalic.woff2?v=3.19") format("woff2"), url("Inter-LightItalic.woff?v=3.19") format("woff");
|
||||||
url("Inter-LightItalic.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-Regular.woff2?v=3.19") format("woff2"),
|
src: url("Inter-Regular.woff2?v=3.19") format("woff2"), url("Inter-Regular.woff?v=3.19") format("woff");
|
||||||
url("Inter-Regular.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-Italic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-Italic.woff2?v=3.19") format("woff2"), url("Inter-Italic.woff?v=3.19") format("woff");
|
||||||
url("Inter-Italic.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-Medium.woff2?v=3.19") format("woff2"),
|
src: url("Inter-Medium.woff2?v=3.19") format("woff2"), url("Inter-Medium.woff?v=3.19") format("woff");
|
||||||
url("Inter-Medium.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-MediumItalic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-MediumItalic.woff2?v=3.19") format("woff2"), url("Inter-MediumItalic.woff?v=3.19") format("woff");
|
||||||
url("Inter-MediumItalic.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-SemiBold.woff2?v=3.19") format("woff2"),
|
src: url("Inter-SemiBold.woff2?v=3.19") format("woff2"), url("Inter-SemiBold.woff?v=3.19") format("woff");
|
||||||
url("Inter-SemiBold.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
@ -101,32 +91,29 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-Bold.woff2?v=3.19") format("woff2"),
|
src: url("Inter-Bold.woff2?v=3.19") format("woff2"), url("Inter-Bold.woff?v=3.19") format("woff");
|
||||||
url("Inter-Bold.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-BoldItalic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-BoldItalic.woff2?v=3.19") format("woff2"), url("Inter-BoldItalic.woff?v=3.19") format("woff");
|
||||||
url("Inter-BoldItalic.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-ExtraBold.woff2?v=3.19") format("woff2"),
|
src: url("Inter-ExtraBold.woff2?v=3.19") format("woff2"), url("Inter-ExtraBold.woff?v=3.19") format("woff");
|
||||||
url("Inter-ExtraBold.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
@ -135,20 +122,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-Black.woff2?v=3.19") format("woff2"),
|
src: url("Inter-Black.woff2?v=3.19") format("woff2"), url("Inter-Black.woff?v=3.19") format("woff");
|
||||||
url("Inter-Black.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-BlackItalic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-BlackItalic.woff2?v=3.19") format("woff2"), url("Inter-BlackItalic.woff?v=3.19") format("woff");
|
||||||
url("Inter-BlackItalic.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------
|
/* -------------------------------------------------------
|
||||||
@ -161,21 +146,20 @@ Usage:
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter var';
|
font-family: "Inter var";
|
||||||
font-weight: 100 900;
|
font-weight: 100 900;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
src: url("Inter-roman.var.woff2?v=3.19") format("woff2");
|
src: url("Inter-roman.var.woff2?v=3.19") format("woff2");
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter var';
|
font-family: "Inter var";
|
||||||
font-weight: 100 900;
|
font-weight: 100 900;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
src: url("Inter-italic.var.woff2?v=3.19") format("woff2");
|
src: url("Inter-italic.var.woff2?v=3.19") format("woff2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
[EXPERIMENTAL] Multi-axis, single variable font.
|
[EXPERIMENTAL] Multi-axis, single variable font.
|
||||||
|
|
||||||
@ -190,7 +174,7 @@ explicitly, e.g.
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter var experimental';
|
font-family: "Inter var experimental";
|
||||||
font-weight: 100 900;
|
font-weight: 100 900;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
font-style: oblique 0deg 10deg;
|
font-style: oblique 0deg 10deg;
|
||||||
|
@ -5,13 +5,16 @@
|
|||||||
* hosted on GitHub, https://GitHub.com/Naereen/Nginx-Fancyindex-Theme
|
* hosted on GitHub, https://GitHub.com/Naereen/Nginx-Fancyindex-Theme
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import url('./fonts/inter.css');
|
@import url("./fonts/inter.css");
|
||||||
|
|
||||||
* { font-family: 'Inter', sans-serif; }
|
* {
|
||||||
@supports (font-variation-settings: normal) {
|
font-family: "Inter", sans-serif;
|
||||||
* { font-family: 'Inter var', sans-serif; }
|
}
|
||||||
|
@supports (font-variation-settings: normal) {
|
||||||
|
* {
|
||||||
|
font-family: "Inter var", sans-serif;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -22,14 +25,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
color: #17191C;
|
color: #17191c;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
line-height: 1.6em;
|
line-height: 1.6em;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: #F4F6FA;
|
background-color: #f4f6fa;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 100px 20px 20px;
|
padding: 100px 20px 20px;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
@ -52,9 +55,11 @@ a {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
a:hover {
|
a:hover {
|
||||||
color: #0DBD8B;
|
color: #0dbd8b;
|
||||||
}
|
}
|
||||||
a.clear, a.clear:link, a.clear:visited {
|
a.clear,
|
||||||
|
a.clear:link,
|
||||||
|
a.clear:visited {
|
||||||
color: #666;
|
color: #666;
|
||||||
padding: 2px 0;
|
padding: 2px 0;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@ -74,7 +79,7 @@ a.clear, a.clear:link, a.clear:visited {
|
|||||||
input {
|
input {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
*overflow: visible;
|
*overflow: visible;
|
||||||
font-family: 'Open Sans', sans-serif;
|
font-family: "Open Sans", sans-serif;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@ -89,20 +94,20 @@ input {
|
|||||||
width: 196px;
|
width: 196px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
|
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
-webkit-transition: border linear .2s,box-shadow linear .2s;
|
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||||
-moz-transition: border linear .2s,box-shadow linear .2s;
|
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||||
-o-transition: border linear .2s,box-shadow linear .2s;
|
-o-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||||
transition: border linear .2s,box-shadow linear .2s;
|
transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||||
}
|
}
|
||||||
input:focus {
|
input:focus {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
border-color: rgba(0,0,0,0.8);
|
border-color: rgba(0, 0, 0, 0.8);
|
||||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(0,0,0,0.6);
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(0, 0, 0, 0.6);
|
||||||
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(0,0,0,0.6);
|
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(0, 0, 0, 0.6);
|
||||||
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(0,0,0,0.6);
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(0, 0, 0, 0.6);
|
||||||
}
|
}
|
||||||
input::-moz-focus-inner {
|
input::-moz-focus-inner {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -128,13 +133,12 @@ tr td:first-of-type {
|
|||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
tr.parent a {
|
tr.parent a {
|
||||||
color: #9099A3;
|
color: #9099a3;
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
th {
|
||||||
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-size: .75em;
|
font-size: 0.75em;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
}
|
}
|
||||||
th + th {
|
th + th {
|
||||||
@ -157,7 +161,8 @@ td {
|
|||||||
-o-transition: background 300ms ease-in;
|
-o-transition: background 300ms ease-in;
|
||||||
transition: background 300ms ease-in;
|
transition: background 300ms ease-in;
|
||||||
}
|
}
|
||||||
td:last-child,th:last-child {
|
td:last-child,
|
||||||
|
th:last-child {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
@ -187,11 +192,11 @@ td a {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: #FFF;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav li a {
|
.nav li a {
|
||||||
color: #17191C;
|
color: #17191c;
|
||||||
display: block;
|
display: block;
|
||||||
padding: 20px 20px;
|
padding: 20px 20px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@ -199,7 +204,7 @@ td a {
|
|||||||
|
|
||||||
.nav li a:hover,
|
.nav li a:hover,
|
||||||
.nav .menu-btn:hover {
|
.nav .menu-btn:hover {
|
||||||
color: #0DBD8B;
|
color: #0dbd8b;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav .logo {
|
.nav .logo {
|
||||||
@ -216,7 +221,7 @@ td a {
|
|||||||
.nav .menu {
|
.nav .menu {
|
||||||
clear: both;
|
clear: both;
|
||||||
max-height: 0;
|
max-height: 0;
|
||||||
transition: max-height .2s ease-out;
|
transition: max-height 0.2s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* menu icon */
|
/* menu icon */
|
||||||
@ -237,18 +242,18 @@ td a {
|
|||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: background .2s ease-out;
|
transition: background 0.2s ease-out;
|
||||||
width: 18px;
|
width: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav .menu-icon .navicon:before,
|
.nav .menu-icon .navicon:before,
|
||||||
.nav .menu-icon .navicon:after {
|
.nav .menu-icon .navicon:after {
|
||||||
background: #17191C;
|
background: #17191c;
|
||||||
content: '';
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transition: all .2s ease-out;
|
transition: all 0.2s ease-out;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,13 +308,13 @@ td a {
|
|||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
padding: 12px 20px;
|
padding: 12px 20px;
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
background-color: #0DBD8B;
|
background-color: #0dbd8b;
|
||||||
color:#FFF;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav .primary:hover {
|
.nav .primary:hover {
|
||||||
background-color: #099970;
|
background-color: #099970;
|
||||||
color:#FFF;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav .menu {
|
.nav .menu {
|
||||||
@ -324,10 +329,10 @@ td a {
|
|||||||
|
|
||||||
footer {
|
footer {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
font-size:0.8em;
|
font-size: 0.8em;
|
||||||
text-align:center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer a {
|
footer a {
|
||||||
color:#03b381;
|
color: #03b381;
|
||||||
}
|
}
|
||||||
|
@ -20,16 +20,16 @@ function errCheck(err?: Error): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const I18N_BASE_PATH = "src/i18n/strings/";
|
const I18N_BASE_PATH = "src/i18n/strings/";
|
||||||
const INCLUDE_LANGS = fs.readdirSync(I18N_BASE_PATH).filter(fn => fn.endsWith(".json"));
|
const INCLUDE_LANGS = fs.readdirSync(I18N_BASE_PATH).filter((fn) => fn.endsWith(".json"));
|
||||||
|
|
||||||
// Ensure lib, lib/i18n and lib/i18n/strings all exist
|
// Ensure lib, lib/i18n and lib/i18n/strings all exist
|
||||||
fs.mkdirSync('lib/i18n/strings', { recursive: true });
|
fs.mkdirSync("lib/i18n/strings", { recursive: true });
|
||||||
|
|
||||||
type Translations = Record<string, Record<string, string> | string>;
|
type Translations = Record<string, Record<string, string> | string>;
|
||||||
|
|
||||||
function genLangFile(file: string, dest: string): void {
|
function genLangFile(file: string, dest: string): void {
|
||||||
const inTrs: Record<string, string> = {};
|
const inTrs: Record<string, string> = {};
|
||||||
[file].forEach(function(f) {
|
[file].forEach(function (f) {
|
||||||
if (fs.existsSync(f)) {
|
if (fs.existsSync(f)) {
|
||||||
try {
|
try {
|
||||||
Object.assign(inTrs, JSON.parse(fs.readFileSync(f).toString()));
|
Object.assign(inTrs, JSON.parse(fs.readFileSync(f).toString()));
|
||||||
@ -68,7 +68,7 @@ function weblateToCounterpart(inTrs: Record<string, string>): Translations {
|
|||||||
const outTrs: Translations = {};
|
const outTrs: Translations = {};
|
||||||
|
|
||||||
for (const key of Object.keys(inTrs)) {
|
for (const key of Object.keys(inTrs)) {
|
||||||
const keyParts = key.split('|', 2);
|
const keyParts = key.split("|", 2);
|
||||||
if (keyParts.length === 2) {
|
if (keyParts.length === 2) {
|
||||||
let obj = outTrs[keyParts[0]];
|
let obj = outTrs[keyParts[0]];
|
||||||
if (obj === undefined) {
|
if (obj === undefined) {
|
||||||
@ -77,7 +77,7 @@ function weblateToCounterpart(inTrs: Record<string, string>): Translations {
|
|||||||
// This is a transitional edge case if a string went from singular to pluralised and both still remain
|
// This is a transitional edge case if a string went from singular to pluralised and both still remain
|
||||||
// in the translation json file. Use the singular translation as `other` and merge pluralisation atop.
|
// in the translation json file. Use the singular translation as `other` and merge pluralisation atop.
|
||||||
obj = outTrs[keyParts[0]] = {
|
obj = outTrs[keyParts[0]] = {
|
||||||
"other": inTrs[key],
|
other: inTrs[key],
|
||||||
};
|
};
|
||||||
console.warn("Found entry in i18n file in both singular and pluralised form", keyParts[0]);
|
console.warn("Found entry in i18n file in both singular and pluralised form", keyParts[0]);
|
||||||
}
|
}
|
||||||
@ -108,10 +108,7 @@ function watchLanguage(file: string, dest: string): void {
|
|||||||
}, 500);
|
}, 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
chokidar.watch(file)
|
chokidar.watch(file).on("add", makeLang).on("change", makeLang).on("error", errCheck);
|
||||||
.on('add', makeLang)
|
|
||||||
.on('change', makeLang)
|
|
||||||
.on('error', errCheck);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// language resources
|
// language resources
|
||||||
@ -121,5 +118,5 @@ INCLUDE_LANGS.forEach((file): void => {
|
|||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
if (watch) {
|
if (watch) {
|
||||||
INCLUDE_LANGS.forEach(file => watchLanguage(I18N_BASE_PATH + file, I18N_DEST));
|
INCLUDE_LANGS.forEach((file) => watchLanguage(I18N_BASE_PATH + file, I18N_DEST));
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const fsProm = require('fs').promises;
|
const fsProm = require("fs").promises;
|
||||||
const path = require('path');
|
const path = require("path");
|
||||||
|
|
||||||
exports.default = async function(context) {
|
exports.default = async function (context) {
|
||||||
const { electronPlatformName, appOutDir } = context;
|
const { electronPlatformName, appOutDir } = context;
|
||||||
|
|
||||||
// Squirrel windows will try to relaunch the app using an executable of the same name as
|
// Squirrel windows will try to relaunch the app using an executable of the same name as
|
||||||
@ -9,7 +9,7 @@ exports.default = async function(context) {
|
|||||||
// We add a fake Riot.exe that it can run which runs the real one.
|
// We add a fake Riot.exe that it can run which runs the real one.
|
||||||
// This also gets signed automatically, presumably because electron-build just looks for all
|
// This also gets signed automatically, presumably because electron-build just looks for all
|
||||||
// exe files and signs them all...
|
// exe files and signs them all...
|
||||||
if (electronPlatformName === 'win32') {
|
if (electronPlatformName === "win32") {
|
||||||
await fsProm.copyFile('build/rebrand_stub/rebrand_stub.exe', path.join(appOutDir, "Riot.exe"));
|
await fsProm.copyFile("build/rebrand_stub/rebrand_stub.exe", path.join(appOutDir, "Riot.exe"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
const { notarize } = require('@electron/notarize');
|
const { notarize } = require("@electron/notarize");
|
||||||
|
|
||||||
let warned = false;
|
let warned = false;
|
||||||
exports.default = async function(context) {
|
exports.default = async function (context) {
|
||||||
const { electronPlatformName, appOutDir } = context;
|
const { electronPlatformName, appOutDir } = context;
|
||||||
const appId = context.packager.info.appInfo.id;
|
const appId = context.packager.info.appInfo.id;
|
||||||
|
|
||||||
if (electronPlatformName === 'darwin') {
|
if (electronPlatformName === "darwin") {
|
||||||
const appName = context.packager.appInfo.productFilename;
|
const appName = context.packager.appInfo.productFilename;
|
||||||
|
|
||||||
const keychainProfile = process.env.NOTARIZE_KEYCHAIN_PROFILE;
|
const keychainProfile = process.env.NOTARIZE_KEYCHAIN_PROFILE;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const { execFile } = require('child_process');
|
const { execFile } = require("child_process");
|
||||||
|
|
||||||
// Loosely based on computeSignToolArgs from app-builder-lib/src/codeSign/windowsCodeSign.ts
|
// Loosely based on computeSignToolArgs from app-builder-lib/src/codeSign/windowsCodeSign.ts
|
||||||
function computeSignToolArgs(options, keyContainer) {
|
function computeSignToolArgs(options, keyContainer) {
|
||||||
@ -8,15 +8,15 @@ function computeSignToolArgs(options, keyContainer) {
|
|||||||
const timestampingServiceUrl = options.options.timeStampServer || "http://timestamp.digicert.com";
|
const timestampingServiceUrl = options.options.timeStampServer || "http://timestamp.digicert.com";
|
||||||
args.push(
|
args.push(
|
||||||
options.isNest || options.hash === "sha256" ? "/tr" : "/t",
|
options.isNest || options.hash === "sha256" ? "/tr" : "/t",
|
||||||
options.isNest || options.hash === "sha256" ? (
|
options.isNest || options.hash === "sha256"
|
||||||
options.options.rfc3161TimeStampServer || "http://timestamp.comodoca.com/rfc3161"
|
? options.options.rfc3161TimeStampServer || "http://timestamp.comodoca.com/rfc3161"
|
||||||
) : timestampingServiceUrl,
|
: timestampingServiceUrl,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
args.push('/kc', keyContainer);
|
args.push("/kc", keyContainer);
|
||||||
// To use the hardware token (this should probably be less hardcoded)
|
// To use the hardware token (this should probably be less hardcoded)
|
||||||
args.push('/csp', 'eToken Base Cryptographic Provider');
|
args.push("/csp", "eToken Base Cryptographic Provider");
|
||||||
// The certificate file. Somehow this appears to be the only way to specify
|
// The certificate file. Somehow this appears to be the only way to specify
|
||||||
// the cert that works. If you specify the subject name or hash, it will
|
// the cert that works. If you specify the subject name or hash, it will
|
||||||
// say it can't associate the private key to the certificate.
|
// say it can't associate the private key to the certificate.
|
||||||
@ -24,7 +24,7 @@ function computeSignToolArgs(options, keyContainer) {
|
|||||||
// so we don't have to hard-code this here
|
// so we don't have to hard-code this here
|
||||||
// fwiw https://stackoverflow.com/questions/17927895/automate-extended-validation-ev-code-signing
|
// fwiw https://stackoverflow.com/questions/17927895/automate-extended-validation-ev-code-signing
|
||||||
// is about the most useful resource on automating code signing...
|
// is about the most useful resource on automating code signing...
|
||||||
args.push('/f', 'element.io\\New_Vector_Ltd.pem');
|
args.push("/f", "element.io\\New_Vector_Ltd.pem");
|
||||||
|
|
||||||
if (options.hash !== "sha1") {
|
if (options.hash !== "sha1") {
|
||||||
args.push("/fd", options.hash);
|
args.push("/fd", options.hash);
|
||||||
@ -47,7 +47,7 @@ function computeSignToolArgs(options, keyContainer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let warned = false;
|
let warned = false;
|
||||||
exports.default = async function(options) {
|
exports.default = async function (options) {
|
||||||
const keyContainer = process.env.SIGNING_KEY_CONTAINER;
|
const keyContainer = process.env.SIGNING_KEY_CONTAINER;
|
||||||
if (keyContainer === undefined) {
|
if (keyContainer === undefined) {
|
||||||
if (!warned) {
|
if (!warned) {
|
||||||
@ -63,9 +63,9 @@ exports.default = async function(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const args = ['sign'].concat(computeSignToolArgs(options, keyContainer));
|
const args = ["sign"].concat(computeSignToolArgs(options, keyContainer));
|
||||||
|
|
||||||
execFile('signtool', args, {}, (error, stdout) => {
|
execFile("signtool", args, {}, (error, stdout) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error("signtool failed with code " + error);
|
console.error("signtool failed with code " + error);
|
||||||
reject("signtool failed with code " + error);
|
reject("signtool failed with code " + error);
|
||||||
|
@ -14,7 +14,7 @@ import { setPackageVersion } from "./set-version";
|
|||||||
const PUB_KEY_URL = "https://packages.riot.im/element-release-key.asc";
|
const PUB_KEY_URL = "https://packages.riot.im/element-release-key.asc";
|
||||||
const PACKAGE_URL_PREFIX = "https://github.com/vector-im/element-web/releases/download/";
|
const PACKAGE_URL_PREFIX = "https://github.com/vector-im/element-web/releases/download/";
|
||||||
const DEVELOP_TGZ_URL = "https://develop.element.io/develop.tar.gz";
|
const DEVELOP_TGZ_URL = "https://develop.element.io/develop.tar.gz";
|
||||||
const ASAR_PATH = 'webapp.asar';
|
const ASAR_PATH = "webapp.asar";
|
||||||
|
|
||||||
async function downloadToFile(url: string, filename: string): Promise<void> {
|
async function downloadToFile(url: string, filename: string): Promise<void> {
|
||||||
console.log("Downloading " + url + "...");
|
console.log("Downloading " + url + "...");
|
||||||
@ -35,7 +35,7 @@ async function downloadToFile(url: string, filename: string): Promise<void> {
|
|||||||
|
|
||||||
async function verifyFile(filename: string): Promise<void> {
|
async function verifyFile(filename: string): Promise<void> {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
childProcess.execFile('gpg', ['--verify', filename + '.asc', filename], (error) => {
|
childProcess.execFile("gpg", ["--verify", filename + ".asc", filename], (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
} else {
|
} else {
|
||||||
@ -48,8 +48,8 @@ async function verifyFile(filename: string): Promise<void> {
|
|||||||
async function main(): Promise<number | undefined> {
|
async function main(): Promise<number | undefined> {
|
||||||
let verify = true;
|
let verify = true;
|
||||||
let importkey = false;
|
let importkey = false;
|
||||||
let pkgDir = 'packages';
|
let pkgDir = "packages";
|
||||||
let deployDir = 'deploys';
|
let deployDir = "deploys";
|
||||||
let cfgDir: string | undefined;
|
let cfgDir: string | undefined;
|
||||||
let targetVersion: string | undefined;
|
let targetVersion: string | undefined;
|
||||||
let filename: string | undefined;
|
let filename: string | undefined;
|
||||||
@ -58,22 +58,22 @@ async function main(): Promise<number | undefined> {
|
|||||||
|
|
||||||
while (process.argv.length > 2) {
|
while (process.argv.length > 2) {
|
||||||
switch (process.argv[2]) {
|
switch (process.argv[2]) {
|
||||||
case '--noverify':
|
case "--noverify":
|
||||||
verify = false;
|
verify = false;
|
||||||
break;
|
break;
|
||||||
case '--importkey':
|
case "--importkey":
|
||||||
importkey = true;
|
importkey = true;
|
||||||
break;
|
break;
|
||||||
case '--packages':
|
case "--packages":
|
||||||
process.argv.shift();
|
process.argv.shift();
|
||||||
pkgDir = process.argv[2];
|
pkgDir = process.argv[2];
|
||||||
break;
|
break;
|
||||||
case '--deploys':
|
case "--deploys":
|
||||||
process.argv.shift();
|
process.argv.shift();
|
||||||
deployDir = process.argv[2];
|
deployDir = process.argv[2];
|
||||||
break;
|
break;
|
||||||
case '--cfgdir':
|
case "--cfgdir":
|
||||||
case '-d':
|
case "-d":
|
||||||
process.argv.shift();
|
process.argv.shift();
|
||||||
cfgDir = process.argv[2];
|
cfgDir = process.argv[2];
|
||||||
break;
|
break;
|
||||||
@ -84,13 +84,13 @@ async function main(): Promise<number | undefined> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (targetVersion === undefined) {
|
if (targetVersion === undefined) {
|
||||||
targetVersion = 'v' + riotDesktopPackageJson.version;
|
targetVersion = "v" + riotDesktopPackageJson.version;
|
||||||
} else if (targetVersion !== 'develop') {
|
} else if (targetVersion !== "develop") {
|
||||||
setVersion = true; // version was specified
|
setVersion = true; // version was specified
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetVersion === 'develop') {
|
if (targetVersion === "develop") {
|
||||||
filename = 'develop.tar.gz';
|
filename = "develop.tar.gz";
|
||||||
url = DEVELOP_TGZ_URL;
|
url = DEVELOP_TGZ_URL;
|
||||||
verify = false; // develop builds aren't signed
|
verify = false; // develop builds aren't signed
|
||||||
} else if (targetVersion.includes("://")) {
|
} else if (targetVersion.includes("://")) {
|
||||||
@ -99,11 +99,11 @@ async function main(): Promise<number | undefined> {
|
|||||||
verify = false; // manually verified
|
verify = false; // manually verified
|
||||||
} else {
|
} else {
|
||||||
filename = `element-${targetVersion}.tar.gz`;
|
filename = `element-${targetVersion}.tar.gz`;
|
||||||
url = PACKAGE_URL_PREFIX + targetVersion + '/' + filename;
|
url = PACKAGE_URL_PREFIX + targetVersion + "/" + filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
const haveGpg = await new Promise<boolean>((resolve) => {
|
const haveGpg = await new Promise<boolean>((resolve) => {
|
||||||
childProcess.execFile('gpg', ['--version'], (error) => {
|
childProcess.execFile("gpg", ["--version"], (error) => {
|
||||||
resolve(!error);
|
resolve(!error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -115,7 +115,7 @@ async function main(): Promise<number | undefined> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await new Promise<boolean>((resolve) => {
|
await new Promise<boolean>((resolve) => {
|
||||||
const gpgProc = childProcess.execFile('gpg', ['--import'], (error) => {
|
const gpgProc = childProcess.execFile("gpg", ["--import"], (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log("Failed to import key", error);
|
console.log("Failed to import key", error);
|
||||||
} else {
|
} else {
|
||||||
@ -123,7 +123,7 @@ async function main(): Promise<number | undefined> {
|
|||||||
}
|
}
|
||||||
resolve(!error);
|
resolve(!error);
|
||||||
});
|
});
|
||||||
fetch(PUB_KEY_URL).then(resp => {
|
fetch(PUB_KEY_URL).then((resp) => {
|
||||||
stream.pipeline(resp.body, gpgProc.stdin!);
|
stream.pipeline(resp.body, gpgProc.stdin!);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -143,13 +143,12 @@ async function main(): Promise<number | undefined> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let haveDeploy = false;
|
let haveDeploy = false;
|
||||||
let expectedDeployDir = path.join(deployDir, path.basename(filename).replace(/\.tar\.gz/, ''));
|
let expectedDeployDir = path.join(deployDir, path.basename(filename).replace(/\.tar\.gz/, ""));
|
||||||
try {
|
try {
|
||||||
await fs.opendir(expectedDeployDir);
|
await fs.opendir(expectedDeployDir);
|
||||||
console.log(expectedDeployDir + "already exists");
|
console.log(expectedDeployDir + "already exists");
|
||||||
haveDeploy = true;
|
haveDeploy = true;
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
}
|
|
||||||
|
|
||||||
if (!haveDeploy) {
|
if (!haveDeploy) {
|
||||||
const outPath = path.join(pkgDir, filename);
|
const outPath = path.join(pkgDir, filename);
|
||||||
@ -167,11 +166,11 @@ async function main(): Promise<number | undefined> {
|
|||||||
|
|
||||||
if (verify) {
|
if (verify) {
|
||||||
try {
|
try {
|
||||||
await fs.stat(outPath+'.asc');
|
await fs.stat(outPath + ".asc");
|
||||||
console.log("Already have " + filename + ".asc: not redownloading");
|
console.log("Already have " + filename + ".asc: not redownloading");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
await downloadToFile(url + '.asc', outPath + '.asc');
|
await downloadToFile(url + ".asc", outPath + ".asc");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Failed to download " + url, e);
|
console.log("Failed to download " + url, e);
|
||||||
return 1;
|
return 1;
|
||||||
@ -192,7 +191,7 @@ async function main(): Promise<number | undefined> {
|
|||||||
await tar.x({
|
await tar.x({
|
||||||
file: outPath,
|
file: outPath,
|
||||||
cwd: deployDir,
|
cwd: deployDir,
|
||||||
onentry: entry => {
|
onentry: (entry) => {
|
||||||
// Find the appropriate extraction path, only needed for `develop` where the dir name is unknown
|
// Find the appropriate extraction path, only needed for `develop` where the dir name is unknown
|
||||||
if (entry.type === "Directory" && !path.join(deployDir, entry.path).startsWith(expectedDeployDir)) {
|
if (entry.type === "Directory" && !path.join(deployDir, entry.path).startsWith(expectedDeployDir)) {
|
||||||
expectedDeployDir = path.join(deployDir, entry.path);
|
expectedDeployDir = path.join(deployDir, entry.path);
|
||||||
@ -205,13 +204,12 @@ async function main(): Promise<number | undefined> {
|
|||||||
await fs.stat(ASAR_PATH);
|
await fs.stat(ASAR_PATH);
|
||||||
console.log(ASAR_PATH + " already present: removing");
|
console.log(ASAR_PATH + " already present: removing");
|
||||||
await fs.unlink(ASAR_PATH);
|
await fs.unlink(ASAR_PATH);
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
}
|
|
||||||
|
|
||||||
if (cfgDir.length) {
|
if (cfgDir.length) {
|
||||||
const configJsonSource = path.join(cfgDir, 'config.json');
|
const configJsonSource = path.join(cfgDir, "config.json");
|
||||||
const configJsonDest = path.join(expectedDeployDir, 'config.json');
|
const configJsonDest = path.join(expectedDeployDir, "config.json");
|
||||||
console.log(configJsonSource + ' -> ' + configJsonDest);
|
console.log(configJsonSource + " -> " + configJsonDest);
|
||||||
await fs.copyFile(configJsonSource, configJsonDest);
|
await fs.copyFile(configJsonSource, configJsonDest);
|
||||||
} else {
|
} else {
|
||||||
console.log("Skipping config file");
|
console.log("Skipping config file");
|
||||||
@ -229,9 +227,11 @@ async function main(): Promise<number | undefined> {
|
|||||||
console.log("Done!");
|
console.log("Done!");
|
||||||
}
|
}
|
||||||
|
|
||||||
main().then((ret) => {
|
main()
|
||||||
|
.then((ret) => {
|
||||||
process.exit(ret);
|
process.exit(ret);
|
||||||
}).catch(e => {
|
})
|
||||||
|
.catch((e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
@ -84,21 +84,21 @@ function humanFileSize(bytes: number, si = false, dp = 1): string {
|
|||||||
const thresh = si ? 1000 : 1024;
|
const thresh = si ? 1000 : 1024;
|
||||||
|
|
||||||
if (Math.abs(bytes) < thresh) {
|
if (Math.abs(bytes) < thresh) {
|
||||||
return bytes + ' B';
|
return bytes + " B";
|
||||||
}
|
}
|
||||||
|
|
||||||
const units = si
|
const units = si
|
||||||
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
? ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
|
||||||
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
: ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
|
||||||
let u = -1;
|
let u = -1;
|
||||||
const r = 10**dp;
|
const r = 10 ** dp;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
bytes /= thresh;
|
bytes /= thresh;
|
||||||
++u;
|
++u;
|
||||||
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
|
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
|
||||||
|
|
||||||
return bytes.toFixed(dp) + ' ' + units[u];
|
return bytes.toFixed(dp) + " " + units[u];
|
||||||
}
|
}
|
||||||
|
|
||||||
const dateTimeOptions: Intl.DateTimeFormatOptions = {
|
const dateTimeOptions: Intl.DateTimeFormatOptions = {
|
||||||
@ -122,7 +122,8 @@ function indexLayout(prefix: string, files: _Object[], dirs: string[]): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (!file.Key ||
|
if (
|
||||||
|
!file.Key ||
|
||||||
HIDDEN_FILES.includes(`/${file.Key}`) ||
|
HIDDEN_FILES.includes(`/${file.Key}`) ||
|
||||||
HIDDEN_FILES.includes(file.Key.slice(file.Key.lastIndexOf("/") + 1))
|
HIDDEN_FILES.includes(file.Key.slice(file.Key.lastIndexOf("/") + 1))
|
||||||
) {
|
) {
|
||||||
@ -143,11 +144,15 @@ function indexLayout(prefix: string, files: _Object[], dirs: string[]): string {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
${rows.map(([link, name, size, date]) => `<tr>
|
${rows
|
||||||
|
.map(
|
||||||
|
([link, name, size, date]) => `<tr>
|
||||||
<td class="link"><a href="${link}">${name}</a></td>
|
<td class="link"><a href="${link}">${name}</a></td>
|
||||||
<td class="size">${size ? humanFileSize(size) : "-"}</td>
|
<td class="size">${size ? humanFileSize(size) : "-"}</td>
|
||||||
<td class="date">${date?.toLocaleString("en-GB", dateTimeOptions) ?? "-"}</td>
|
<td class="date">${date?.toLocaleString("en-GB", dateTimeOptions) ?? "-"}</td>
|
||||||
</tr>`).join("")}
|
</tr>`,
|
||||||
|
)
|
||||||
|
.join("")}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
`);
|
`);
|
||||||
@ -166,17 +171,20 @@ async function generateIndex(Prefix: string): Promise<{
|
|||||||
|
|
||||||
const listResponse = await client.send(command);
|
const listResponse = await client.send(command);
|
||||||
const files = listResponse.Contents ?? [];
|
const files = listResponse.Contents ?? [];
|
||||||
const dirs = listResponse.CommonPrefixes
|
const dirs =
|
||||||
?.map(p => p.Prefix?.slice(Prefix.length).split("/", 2)[0])
|
(listResponse.CommonPrefixes?.map((p) => p.Prefix?.slice(Prefix.length).split("/", 2)[0]).filter(
|
||||||
.filter(Boolean) as string[] ?? [];
|
Boolean,
|
||||||
|
) as string[]) ?? [];
|
||||||
const Body = indexLayout(Prefix, files, dirs);
|
const Body = indexLayout(Prefix, files, dirs);
|
||||||
|
|
||||||
await client.send(new PutObjectCommand({
|
await client.send(
|
||||||
|
new PutObjectCommand({
|
||||||
Body,
|
Body,
|
||||||
Bucket,
|
Bucket,
|
||||||
ContentType: "text/html",
|
ContentType: "text/html",
|
||||||
Key: Prefix + "index.html",
|
Key: Prefix + "index.html",
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
return { files, dirs };
|
return { files, dirs };
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
hak
|
# hak
|
||||||
===
|
|
||||||
|
|
||||||
This tool builds native dependencies for element-desktop. Here follows some very minimal
|
This tool builds native dependencies for element-desktop. Here follows some very minimal
|
||||||
documentation for it.
|
documentation for it.
|
||||||
|
|
||||||
Goals:
|
Goals:
|
||||||
* Must build compiled native node modules in a shippable state
|
|
||||||
|
- Must build compiled native node modules in a shippable state
|
||||||
(ie. only dynamically linked against libraries that will be on the
|
(ie. only dynamically linked against libraries that will be on the
|
||||||
target system, all unnecessary files removed).
|
target system, all unnecessary files removed).
|
||||||
* Must be able to build any native module, no matter what build system
|
- Must be able to build any native module, no matter what build system
|
||||||
it uses (electron-rebuild is supposed to do this job but only works
|
it uses (electron-rebuild is supposed to do this job but only works
|
||||||
for modules that use gyp).
|
for modules that use gyp).
|
||||||
|
|
||||||
@ -16,25 +16,25 @@ It's also loosely designed to be a general tool and agnostic to what it's
|
|||||||
actually building. It's used here to build modules for the electron app
|
actually building. It's used here to build modules for the electron app
|
||||||
but should work equally well for building modules for normal node.
|
but should work equally well for building modules for normal node.
|
||||||
|
|
||||||
Running
|
# Running
|
||||||
=======
|
|
||||||
Hak is invoked with a command and a dependency, eg. `yarn run hak fetch matrix-seshat`.
|
Hak is invoked with a command and a dependency, eg. `yarn run hak fetch matrix-seshat`.
|
||||||
If no dependencies are given, hak runs the command on all dependencies.
|
If no dependencies are given, hak runs the command on all dependencies.
|
||||||
|
|
||||||
Files
|
# Files
|
||||||
=====
|
|
||||||
There are a lot of files involved:
|
There are a lot of files involved:
|
||||||
|
|
||||||
* scripts/hak/... - The tool itself
|
- scripts/hak/... - The tool itself
|
||||||
* hak/[dependency] - Files provided by the app that tell hak how to build each of its native dependencies.
|
- hak/[dependency] - Files provided by the app that tell hak how to build each of its native dependencies.
|
||||||
Contains a hak.json file and also some script files, each of which must be referenced in hak.json.
|
Contains a hak.json file and also some script files, each of which must be referenced in hak.json.
|
||||||
* .hak/ - Files generated by hak in the course of doing its job. Includes the dependency module itself and
|
- .hak/ - Files generated by hak in the course of doing its job. Includes the dependency module itself and
|
||||||
any of the native dependency's native dependencies.
|
any of the native dependency's native dependencies.
|
||||||
* .hak/[dependency]/build - An extracted copy of the dependency's node module used to build it.
|
- .hak/[dependency]/build - An extracted copy of the dependency's node module used to build it.
|
||||||
* .hak/[dependency]/out - Another extracted copy of the dependency, this one contains only what will be shipped.
|
- .hak/[dependency]/out - Another extracted copy of the dependency, this one contains only what will be shipped.
|
||||||
|
|
||||||
|
# Workings
|
||||||
|
|
||||||
Workings
|
|
||||||
========
|
|
||||||
Hak works around native node modules that try to fetch or build their native component in
|
Hak works around native node modules that try to fetch or build their native component in
|
||||||
the npm 'install' phase - modules that do this will typically end up with native components
|
the npm 'install' phase - modules that do this will typically end up with native components
|
||||||
targeted to the build platform and the node that npm/yarn is using, which is no good for an
|
targeted to the build platform and the node that npm/yarn is using, which is no good for an
|
||||||
@ -49,33 +49,34 @@ This also means that the dependencies cannot be listed in `dependencies` or
|
|||||||
try to fetch their native parts. Instead, they are listed in `hakDependencies` which
|
try to fetch their native parts. Instead, they are listed in `hakDependencies` which
|
||||||
hak reads to install them for you.
|
hak reads to install them for you.
|
||||||
|
|
||||||
Hak will *not* install dependencies for the copy of the module it links into your
|
Hak will _not_ install dependencies for the copy of the module it links into your
|
||||||
project, so if your native module has javascript dependencies that are actually needed at
|
project, so if your native module has javascript dependencies that are actually needed at
|
||||||
runtime (and not just to fetch / build the native parts), it won't work.
|
runtime (and not just to fetch / build the native parts), it won't work.
|
||||||
|
|
||||||
Hak will generate a `.yarnrc` in the project directory to set the link directory to its
|
Hak will generate a `.yarnrc` in the project directory to set the link directory to its
|
||||||
own in the .hak directory (unless one already exists, in which case this is your problem).
|
own in the .hak directory (unless one already exists, in which case this is your problem).
|
||||||
|
|
||||||
Lifecycle
|
# Lifecycle
|
||||||
=========
|
|
||||||
Hak is divided into lifecycle stages, in order:
|
Hak is divided into lifecycle stages, in order:
|
||||||
* fetch - Download and extract the source of the dependency
|
|
||||||
* link - Link the copy of the dependency into your node_modules directory
|
- fetch - Download and extract the source of the dependency
|
||||||
* fetchDeps - Fetch & extract any native dependencies required to build the module.
|
- link - Link the copy of the dependency into your node_modules directory
|
||||||
* build - The Good Stuff. Configure and build any native dependencies, then the module itself.
|
- fetchDeps - Fetch & extract any native dependencies required to build the module.
|
||||||
* copy - Copy the built artifact from the module build directory to the module output directory.
|
- build - The Good Stuff. Configure and build any native dependencies, then the module itself.
|
||||||
|
- copy - Copy the built artifact from the module build directory to the module output directory.
|
||||||
|
|
||||||
|
# hak.json
|
||||||
|
|
||||||
hak.json
|
|
||||||
========
|
|
||||||
The scripts section contains scripts used for lifecycle stages that need them (fetch, fetchDeps, build).
|
The scripts section contains scripts used for lifecycle stages that need them (fetch, fetchDeps, build).
|
||||||
It also contains 'prune' and 'copy' which are globs of files to delete from the output module directory
|
It also contains 'prune' and 'copy' which are globs of files to delete from the output module directory
|
||||||
and copy over from the module build directory to the output module directory, respectively.
|
and copy over from the module build directory to the output module directory, respectively.
|
||||||
|
|
||||||
Shortcomings
|
# Shortcomings
|
||||||
============
|
|
||||||
Hak doesn't know about dependencies between lifecycle stages, ie. it doesn't know that you need to
|
Hak doesn't know about dependencies between lifecycle stages, ie. it doesn't know that you need to
|
||||||
'fetch' and 'fetchDeps' before you can 'build', etc. You get to run each individually, and remember
|
'fetch' and 'fetchDeps' before you can 'build', etc. You get to run each individually, and remember
|
||||||
the right order.
|
the right order.
|
||||||
|
|
||||||
There is also a *lot* of duplication in the command execution: we should abstract away
|
There is also a _lot_ of duplication in the command execution: we should abstract away
|
||||||
some of the boilerplate required to run commands & so forth.
|
some of the boilerplate required to run commands & so forth.
|
||||||
|
@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import rimraf from 'rimraf';
|
import rimraf from "rimraf";
|
||||||
|
|
||||||
import { DependencyInfo } from './dep';
|
import { DependencyInfo } from "./dep";
|
||||||
import HakEnv from './hakEnv';
|
import HakEnv from "./hakEnv";
|
||||||
|
|
||||||
export default async function clean(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function clean(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
@ -32,7 +32,7 @@ export default async function clean(hakEnv: HakEnv, moduleInfo: DependencyInfo):
|
|||||||
});
|
});
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
rimraf(path.join(hakEnv.dotHakDir, 'links', moduleInfo.name), (err?: Error | null) => {
|
rimraf(path.join(hakEnv.dotHakDir, "links", moduleInfo.name), (err?: Error | null) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
@ -42,7 +42,7 @@ export default async function clean(hakEnv: HakEnv, moduleInfo: DependencyInfo):
|
|||||||
});
|
});
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
rimraf(path.join(hakEnv.projectRoot, 'node_modules', moduleInfo.name), (err?: Error | null) => {
|
rimraf(path.join(hakEnv.projectRoot, "node_modules", moduleInfo.name), (err?: Error | null) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
|
@ -14,15 +14,15 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import fsProm from 'fs/promises';
|
import fsProm from "fs/promises";
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
import rimraf from 'rimraf';
|
import rimraf from "rimraf";
|
||||||
import glob from 'glob';
|
import glob from "glob";
|
||||||
import mkdirp from 'mkdirp';
|
import mkdirp from "mkdirp";
|
||||||
|
|
||||||
import HakEnv from './hakEnv';
|
import HakEnv from "./hakEnv";
|
||||||
import { DependencyInfo } from './dep';
|
import { DependencyInfo } from "./dep";
|
||||||
|
|
||||||
export default async function copy(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function copy(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
if (moduleInfo.cfg.prune) {
|
if (moduleInfo.cfg.prune) {
|
||||||
@ -34,7 +34,7 @@ export default async function copy(hakEnv: HakEnv, moduleInfo: DependencyInfo):
|
|||||||
await mkdirp(moduleInfo.moduleOutDir);
|
await mkdirp(moduleInfo.moduleOutDir);
|
||||||
process.chdir(moduleInfo.moduleOutDir);
|
process.chdir(moduleInfo.moduleOutDir);
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
rimraf(moduleInfo.cfg.prune, {}, err => {
|
rimraf(moduleInfo.cfg.prune, {}, (err) => {
|
||||||
err ? reject(err) : resolve();
|
err ? reject(err) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -48,46 +48,44 @@ export default async function copy(hakEnv: HakEnv, moduleInfo: DependencyInfo):
|
|||||||
// is the same as moduleBuildDirs[0], so we're just listing the contents
|
// is the same as moduleBuildDirs[0], so we're just listing the contents
|
||||||
// of the first one.
|
// of the first one.
|
||||||
const files = await new Promise<string[]>((resolve, reject) => {
|
const files = await new Promise<string[]>((resolve, reject) => {
|
||||||
glob(moduleInfo.cfg.copy, {
|
glob(
|
||||||
|
moduleInfo.cfg.copy,
|
||||||
|
{
|
||||||
nosort: true,
|
nosort: true,
|
||||||
silent: true,
|
silent: true,
|
||||||
cwd: moduleInfo.moduleBuildDir,
|
cwd: moduleInfo.moduleBuildDir,
|
||||||
}, (err, files) => {
|
},
|
||||||
|
(err, files) => {
|
||||||
err ? reject(err) : resolve(files);
|
err ? reject(err) : resolve(files);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (moduleInfo.moduleBuildDirs.length > 1) {
|
if (moduleInfo.moduleBuildDirs.length > 1) {
|
||||||
if (!hakEnv.isMac()) {
|
if (!hakEnv.isMac()) {
|
||||||
console.error(
|
console.error(
|
||||||
"You asked me to copy multiple targets but I've only been taught " +
|
"You asked me to copy multiple targets but I've only been taught " + "how to do that on macOS.",
|
||||||
"how to do that on macOS.",
|
|
||||||
);
|
);
|
||||||
throw new Error("Can't copy multiple targets on this platform");
|
throw new Error("Can't copy multiple targets on this platform");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const f of files) {
|
for (const f of files) {
|
||||||
const components = moduleInfo.moduleBuildDirs.map(dir => path.join(dir, f));
|
const components = moduleInfo.moduleBuildDirs.map((dir) => path.join(dir, f));
|
||||||
const dst = path.join(moduleInfo.moduleOutDir, f);
|
const dst = path.join(moduleInfo.moduleOutDir, f);
|
||||||
|
|
||||||
await mkdirp(path.dirname(dst));
|
await mkdirp(path.dirname(dst));
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
childProcess.execFile('lipo',
|
childProcess.execFile("lipo", ["-create", "-output", dst, ...components], (err) => {
|
||||||
['-create', '-output', dst, ...components], (err) => {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(
|
console.log("Copying files from " + moduleInfo.moduleBuildDir + " to " + moduleInfo.moduleOutDir);
|
||||||
"Copying files from " +
|
|
||||||
moduleInfo.moduleBuildDir + " to " + moduleInfo.moduleOutDir,
|
|
||||||
);
|
|
||||||
for (const f of files) {
|
for (const f of files) {
|
||||||
console.log("\t" + f);
|
console.log("\t" + f);
|
||||||
const src = path.join(moduleInfo.moduleBuildDir, f);
|
const src = path.join(moduleInfo.moduleBuildDir, f);
|
||||||
|
@ -28,5 +28,5 @@ export interface DependencyInfo {
|
|||||||
moduleOutDir: string;
|
moduleOutDir: string;
|
||||||
nodeModuleBinDir: string;
|
nodeModuleBinDir: string;
|
||||||
depPrefix: string;
|
depPrefix: string;
|
||||||
scripts: Record<string, (hakEnv: HakEnv, moduleInfo: DependencyInfo) => Promise<void> >;
|
scripts: Record<string, (hakEnv: HakEnv, moduleInfo: DependencyInfo) => Promise<void>>;
|
||||||
}
|
}
|
||||||
|
@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import fsProm from 'fs/promises';
|
import fsProm from "fs/promises";
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
import pacote from 'pacote';
|
import pacote from "pacote";
|
||||||
|
|
||||||
import HakEnv from './hakEnv';
|
import HakEnv from "./hakEnv";
|
||||||
import { DependencyInfo } from './dep';
|
import { DependencyInfo } from "./dep";
|
||||||
|
|
||||||
export default async function fetch(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function fetch(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
let haveModuleBuildDir;
|
let haveModuleBuildDir;
|
||||||
@ -41,15 +41,11 @@ export default async function fetch(hakEnv: HakEnv, moduleInfo: DependencyInfo):
|
|||||||
|
|
||||||
console.log("Running yarn install in " + moduleInfo.moduleBuildDir);
|
console.log("Running yarn install in " + moduleInfo.moduleBuildDir);
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn(hakEnv.isWin() ? "yarn.cmd" : "yarn", ["install", "--ignore-scripts"], {
|
||||||
hakEnv.isWin() ? 'yarn.cmd' : 'yarn',
|
stdio: "inherit",
|
||||||
['install', '--ignore-scripts'],
|
|
||||||
{
|
|
||||||
stdio: 'inherit',
|
|
||||||
cwd: moduleInfo.moduleBuildDir,
|
cwd: moduleInfo.moduleBuildDir,
|
||||||
},
|
});
|
||||||
);
|
proc.on("exit", (code) => {
|
||||||
proc.on('exit', code => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -14,10 +14,10 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import mkdirp from 'mkdirp';
|
import mkdirp from "mkdirp";
|
||||||
|
|
||||||
import { DependencyInfo } from './dep';
|
import { DependencyInfo } from "./dep";
|
||||||
import HakEnv from './hakEnv';
|
import HakEnv from "./hakEnv";
|
||||||
|
|
||||||
export default async function fetchDeps(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function fetchDeps(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
await mkdirp(moduleInfo.moduleDotHakDir);
|
await mkdirp(moduleInfo.moduleDotHakDir);
|
||||||
|
@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import os from 'os';
|
import os from "os";
|
||||||
import nodePreGypVersioning from "node-pre-gyp/lib/util/versioning";
|
import nodePreGypVersioning from "node-pre-gyp/lib/util/versioning";
|
||||||
import { getElectronVersion } from "app-builder-lib/out/electron/electronVersion";
|
import { getElectronVersion } from "app-builder-lib/out/electron/electronVersion";
|
||||||
|
|
||||||
import { Arch, Target, TARGETS, getHost, isHostId, TargetId } from './target';
|
import { Arch, Target, TARGETS, getHost, isHostId, TargetId } from "./target";
|
||||||
|
|
||||||
async function getRuntime(projectRoot: string): Promise<string> {
|
async function getRuntime(projectRoot: string): Promise<string> {
|
||||||
const electronVersion = await getElectronVersion(projectRoot);
|
const electronVersion = await getElectronVersion(projectRoot);
|
||||||
return electronVersion ? 'electron' : 'node-webkit';
|
return electronVersion ? "electron" : "node-webkit";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getRuntimeVersion(projectRoot: string): Promise<string> {
|
async function getRuntimeVersion(projectRoot: string): Promise<string> {
|
||||||
@ -48,7 +48,7 @@ export default class HakEnv {
|
|||||||
throw new Error(`Unknown target ${targetId}!`);
|
throw new Error(`Unknown target ${targetId}!`);
|
||||||
}
|
}
|
||||||
this.target = target;
|
this.target = target;
|
||||||
this.dotHakDir = path.join(this.projectRoot, '.hak');
|
this.dotHakDir = path.join(this.projectRoot, ".hak");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(): Promise<void> {
|
public async init(): Promise<void> {
|
||||||
@ -62,7 +62,7 @@ export default class HakEnv {
|
|||||||
|
|
||||||
// {node_abi}-{platform}-{arch}
|
// {node_abi}-{platform}-{arch}
|
||||||
public getNodeTriple(): string {
|
public getNodeTriple(): string {
|
||||||
return this.getRuntimeAbi() + '-' + this.target.platform + '-' + this.target.arch;
|
return this.getRuntimeAbi() + "-" + this.target.platform + "-" + this.target.arch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTargetId(): TargetId {
|
public getTargetId(): TargetId {
|
||||||
@ -70,15 +70,15 @@ export default class HakEnv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public isWin(): boolean {
|
public isWin(): boolean {
|
||||||
return this.target.platform === 'win32';
|
return this.target.platform === "win32";
|
||||||
}
|
}
|
||||||
|
|
||||||
public isMac(): boolean {
|
public isMac(): boolean {
|
||||||
return this.target.platform === 'darwin';
|
return this.target.platform === "darwin";
|
||||||
}
|
}
|
||||||
|
|
||||||
public isLinux(): boolean {
|
public isLinux(): boolean {
|
||||||
return this.target.platform === 'linux';
|
return this.target.platform === "linux";
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTargetArch(): Arch {
|
public getTargetArch(): Arch {
|
||||||
@ -93,7 +93,7 @@ export default class HakEnv {
|
|||||||
return Object.assign({}, process.env, {
|
return Object.assign({}, process.env, {
|
||||||
npm_config_arch: this.target.arch,
|
npm_config_arch: this.target.arch,
|
||||||
npm_config_target_arch: this.target.arch,
|
npm_config_target_arch: this.target.arch,
|
||||||
npm_config_disturl: 'https://electronjs.org/headers',
|
npm_config_disturl: "https://electronjs.org/headers",
|
||||||
npm_config_runtime: this.runtime,
|
npm_config_runtime: this.runtime,
|
||||||
npm_config_target: this.runtimeVersion,
|
npm_config_target: this.runtimeVersion,
|
||||||
npm_config_build_from_source: true,
|
npm_config_build_from_source: true,
|
||||||
@ -102,7 +102,7 @@ export default class HakEnv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public wantsStaticSqlCipherUnix(): boolean {
|
public wantsStaticSqlCipherUnix(): boolean {
|
||||||
return this.isMac() || process.env.SQLCIPHER_STATIC == '1';
|
return this.isMac() || process.env.SQLCIPHER_STATIC == "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
public wantsStaticSqlCipher(): boolean {
|
public wantsStaticSqlCipher(): boolean {
|
||||||
|
@ -14,42 +14,27 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import findNpmPrefix from 'find-npm-prefix';
|
import findNpmPrefix from "find-npm-prefix";
|
||||||
|
|
||||||
import HakEnv from './hakEnv';
|
import HakEnv from "./hakEnv";
|
||||||
import { TargetId } from './target';
|
import { TargetId } from "./target";
|
||||||
import { DependencyInfo } from './dep';
|
import { DependencyInfo } from "./dep";
|
||||||
|
|
||||||
const GENERALCOMMANDS = [
|
const GENERALCOMMANDS = ["target"];
|
||||||
'target',
|
|
||||||
];
|
|
||||||
|
|
||||||
// These can only be run on specific modules
|
// These can only be run on specific modules
|
||||||
const MODULECOMMANDS = [
|
const MODULECOMMANDS = ["check", "fetch", "link", "fetchDeps", "build", "copy", "clean"];
|
||||||
'check',
|
|
||||||
'fetch',
|
|
||||||
'link',
|
|
||||||
'fetchDeps',
|
|
||||||
'build',
|
|
||||||
'copy',
|
|
||||||
'clean',
|
|
||||||
];
|
|
||||||
|
|
||||||
// Shortcuts for multiple commands at once (useful for building universal binaries
|
// Shortcuts for multiple commands at once (useful for building universal binaries
|
||||||
// because you can run the fetch/fetchDeps/build for each arch and then copy/link once)
|
// because you can run the fetch/fetchDeps/build for each arch and then copy/link once)
|
||||||
const METACOMMANDS: Record<string, string[]> = {
|
const METACOMMANDS: Record<string, string[]> = {
|
||||||
'fetchandbuild': ['check', 'fetch', 'fetchDeps', 'build'],
|
fetchandbuild: ["check", "fetch", "fetchDeps", "build"],
|
||||||
'copyandlink': ['copy', 'link'],
|
copyandlink: ["copy", "link"],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Scripts valid in a hak.json 'scripts' section
|
// Scripts valid in a hak.json 'scripts' section
|
||||||
const HAKSCRIPTS = [
|
const HAKSCRIPTS = ["check", "fetch", "fetchDeps", "build"];
|
||||||
'check',
|
|
||||||
'fetch',
|
|
||||||
'fetchDeps',
|
|
||||||
'build',
|
|
||||||
];
|
|
||||||
|
|
||||||
async function main(): Promise<void> {
|
async function main(): Promise<void> {
|
||||||
const prefix = await findNpmPrefix(process.cwd());
|
const prefix = await findNpmPrefix(process.cwd());
|
||||||
@ -65,11 +50,12 @@ async function main(): Promise<void> {
|
|||||||
// Apply `--target <target>` option if specified
|
// Apply `--target <target>` option if specified
|
||||||
// Can be specified multiple times for the copy command to bundle
|
// Can be specified multiple times for the copy command to bundle
|
||||||
// multiple archs into a single universal output module)
|
// multiple archs into a single universal output module)
|
||||||
while (true) { // eslint-disable-line no-constant-condition
|
for (;;) {
|
||||||
const targetIndex = process.argv.indexOf('--target');
|
// eslint-disable-line no-constant-condition
|
||||||
|
const targetIndex = process.argv.indexOf("--target");
|
||||||
if (targetIndex === -1) break;
|
if (targetIndex === -1) break;
|
||||||
|
|
||||||
if ((targetIndex + 1) >= process.argv.length) {
|
if (targetIndex + 1 >= process.argv.length) {
|
||||||
console.error("--target option specified without a target");
|
console.error("--target option specified without a target");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@ -77,7 +63,7 @@ async function main(): Promise<void> {
|
|||||||
targetIds.push(process.argv.splice(targetIndex, 2)[1] as TargetId);
|
targetIds.push(process.argv.splice(targetIndex, 2)[1] as TargetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hakEnvs = targetIds.map(tid => new HakEnv(prefix, tid));
|
const hakEnvs = targetIds.map((tid) => new HakEnv(prefix, tid));
|
||||||
if (hakEnvs.length == 0) hakEnvs.push(new HakEnv(prefix, null));
|
if (hakEnvs.length == 0) hakEnvs.push(new HakEnv(prefix, null));
|
||||||
for (const h of hakEnvs) {
|
for (const h of hakEnvs) {
|
||||||
await h.init();
|
await h.init();
|
||||||
@ -89,7 +75,7 @@ async function main(): Promise<void> {
|
|||||||
const hakDepsCfg = packageJson.hakDependencies || {};
|
const hakDepsCfg = packageJson.hakDependencies || {};
|
||||||
|
|
||||||
for (const dep of Object.keys(hakDepsCfg)) {
|
for (const dep of Object.keys(hakDepsCfg)) {
|
||||||
const hakJsonPath = path.join(prefix, 'hak', dep, 'hak.json');
|
const hakJsonPath = path.join(prefix, "hak", dep, "hak.json");
|
||||||
let hakJson: Record<string, any>;
|
let hakJson: Record<string, any>;
|
||||||
try {
|
try {
|
||||||
hakJson = await require(hakJsonPath);
|
hakJson = await require(hakJsonPath);
|
||||||
@ -102,20 +88,20 @@ async function main(): Promise<void> {
|
|||||||
name: dep,
|
name: dep,
|
||||||
version: hakDepsCfg[dep],
|
version: hakDepsCfg[dep],
|
||||||
cfg: hakJson,
|
cfg: hakJson,
|
||||||
moduleHakDir: path.join(prefix, 'hak', dep),
|
moduleHakDir: path.join(prefix, "hak", dep),
|
||||||
moduleDotHakDir: path.join(hakEnv.dotHakDir, dep),
|
moduleDotHakDir: path.join(hakEnv.dotHakDir, dep),
|
||||||
moduleTargetDotHakDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId()),
|
moduleTargetDotHakDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId()),
|
||||||
moduleBuildDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), 'build'),
|
moduleBuildDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), "build"),
|
||||||
moduleBuildDirs: hakEnvs.map(h => path.join(h.dotHakDir, dep, h.getTargetId(), 'build')),
|
moduleBuildDirs: hakEnvs.map((h) => path.join(h.dotHakDir, dep, h.getTargetId(), "build")),
|
||||||
moduleOutDir: path.join(hakEnv.dotHakDir, 'hakModules', dep),
|
moduleOutDir: path.join(hakEnv.dotHakDir, "hakModules", dep),
|
||||||
nodeModuleBinDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), 'build', 'node_modules', '.bin'),
|
nodeModuleBinDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), "build", "node_modules", ".bin"),
|
||||||
depPrefix: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), 'opt'),
|
depPrefix: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), "opt"),
|
||||||
scripts: {},
|
scripts: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const s of HAKSCRIPTS) {
|
for (const s of HAKSCRIPTS) {
|
||||||
if (hakJson.scripts && hakJson.scripts[s]) {
|
if (hakJson.scripts && hakJson.scripts[s]) {
|
||||||
const scriptModule = await import(path.join(prefix, 'hak', dep, hakJson.scripts[s]));
|
const scriptModule = await import(path.join(prefix, "hak", dep, hakJson.scripts[s]));
|
||||||
if (scriptModule.__esModule) {
|
if (scriptModule.__esModule) {
|
||||||
deps[dep].scripts[s] = scriptModule.default;
|
deps[dep].scripts[s] = scriptModule.default;
|
||||||
} else {
|
} else {
|
||||||
@ -127,14 +113,14 @@ async function main(): Promise<void> {
|
|||||||
|
|
||||||
let cmds: string[];
|
let cmds: string[];
|
||||||
if (process.argv.length < 3) {
|
if (process.argv.length < 3) {
|
||||||
cmds = ['check', 'fetch', 'fetchDeps', 'build', 'copy', 'link'];
|
cmds = ["check", "fetch", "fetchDeps", "build", "copy", "link"];
|
||||||
} else if (METACOMMANDS[process.argv[2]]) {
|
} else if (METACOMMANDS[process.argv[2]]) {
|
||||||
cmds = METACOMMANDS[process.argv[2]];
|
cmds = METACOMMANDS[process.argv[2]];
|
||||||
} else {
|
} else {
|
||||||
cmds = [process.argv[2]];
|
cmds = [process.argv[2]];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hakEnvs.length > 1 && cmds.some(c => !['copy', 'link'].includes(c))) {
|
if (hakEnvs.length > 1 && cmds.some((c) => !["copy", "link"].includes(c))) {
|
||||||
// We allow link here too for convenience because it's completely arch independent
|
// We allow link here too for convenience because it's completely arch independent
|
||||||
console.error("Multiple targets only supported with the copy command");
|
console.error("Multiple targets only supported with the copy command");
|
||||||
return;
|
return;
|
||||||
@ -145,7 +131,7 @@ async function main(): Promise<void> {
|
|||||||
|
|
||||||
for (const cmd of cmds) {
|
for (const cmd of cmds) {
|
||||||
if (GENERALCOMMANDS.includes(cmd)) {
|
if (GENERALCOMMANDS.includes(cmd)) {
|
||||||
if (cmd === 'target') {
|
if (cmd === "target") {
|
||||||
console.log(hakEnv.getNodeTriple());
|
console.log(hakEnv.getNodeTriple());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -160,15 +146,12 @@ async function main(): Promise<void> {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const cmdFunc = (await import('./' + cmd)).default;
|
const cmdFunc = (await import("./" + cmd)).default;
|
||||||
|
|
||||||
for (const mod of modules) {
|
for (const mod of modules) {
|
||||||
const depInfo = deps[mod];
|
const depInfo = deps[mod];
|
||||||
if (depInfo === undefined) {
|
if (depInfo === undefined) {
|
||||||
console.log(
|
console.log("Module " + mod + " not found - is it in hakDependencies " + "in your package.json?");
|
||||||
"Module " + mod + " not found - is it in hakDependencies " +
|
|
||||||
"in your package.json?",
|
|
||||||
);
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
console.log("hak " + cmd + ": " + mod);
|
console.log("hak " + cmd + ": " + mod);
|
||||||
@ -177,7 +160,7 @@ async function main(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch(err => {
|
main().catch((err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import os from 'os';
|
import os from "os";
|
||||||
import fsProm from 'fs/promises';
|
import fsProm from "fs/promises";
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
|
|
||||||
import HakEnv from './hakEnv';
|
import HakEnv from "./hakEnv";
|
||||||
import { DependencyInfo } from './dep';
|
import { DependencyInfo } from "./dep";
|
||||||
|
|
||||||
export default async function link(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function link(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
const yarnrc = path.join(hakEnv.projectRoot, '.yarnrc');
|
const yarnrc = path.join(hakEnv.projectRoot, ".yarnrc");
|
||||||
// this is fairly terrible but it's reasonably clunky to either parse a yarnrc
|
// this is fairly terrible but it's reasonably clunky to either parse a yarnrc
|
||||||
// properly or get yarn to do it, so this will probably suffice for now.
|
// properly or get yarn to do it, so this will probably suffice for now.
|
||||||
// We just check to see if there is a local .yarnrc at all, and assume that
|
// We just check to see if there is a local .yarnrc at all, and assume that
|
||||||
@ -43,28 +43,28 @@ export default async function link(hakEnv: HakEnv, moduleInfo: DependencyInfo):
|
|||||||
// (ie. Windows absolute paths) but strings in quotes get parsed as
|
// (ie. Windows absolute paths) but strings in quotes get parsed as
|
||||||
// JSON so need to be valid JSON encoded strings (ie. have the
|
// JSON so need to be valid JSON encoded strings (ie. have the
|
||||||
// backslashes escaped). JSON.stringify will add quotes and escape.
|
// backslashes escaped). JSON.stringify will add quotes and escape.
|
||||||
'--link-folder ' + JSON.stringify(path.join(hakEnv.dotHakDir, 'links')) + os.EOL,
|
"--link-folder " + JSON.stringify(path.join(hakEnv.dotHakDir, "links")) + os.EOL,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const yarnCmd = 'yarn' + (hakEnv.isWin() ? '.cmd' : '');
|
const yarnCmd = "yarn" + (hakEnv.isWin() ? ".cmd" : "");
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(yarnCmd, ['link'], {
|
const proc = childProcess.spawn(yarnCmd, ["link"], {
|
||||||
cwd: moduleInfo.moduleOutDir,
|
cwd: moduleInfo.moduleOutDir,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
});
|
});
|
||||||
proc.on('exit', code => {
|
proc.on("exit", (code) => {
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(yarnCmd, ['link', moduleInfo.name], {
|
const proc = childProcess.spawn(yarnCmd, ["link", moduleInfo.name], {
|
||||||
cwd: hakEnv.projectRoot,
|
cwd: hakEnv.projectRoot,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
});
|
});
|
||||||
proc.on('exit', code => {
|
proc.on("exit", (code) => {
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -20,29 +20,29 @@ import { GLIBC, MUSL, family as processLibC } from "detect-libc";
|
|||||||
// details in a single string.
|
// details in a single string.
|
||||||
// See https://doc.rust-lang.org/rustc/platform-support.html.
|
// See https://doc.rust-lang.org/rustc/platform-support.html.
|
||||||
export type TargetId =
|
export type TargetId =
|
||||||
'aarch64-apple-darwin' |
|
| "aarch64-apple-darwin"
|
||||||
'x86_64-apple-darwin' |
|
| "x86_64-apple-darwin"
|
||||||
'universal-apple-darwin' |
|
| "universal-apple-darwin"
|
||||||
'i686-pc-windows-msvc' |
|
| "i686-pc-windows-msvc"
|
||||||
'x86_64-pc-windows-msvc' |
|
| "x86_64-pc-windows-msvc"
|
||||||
'i686-unknown-linux-musl' |
|
| "i686-unknown-linux-musl"
|
||||||
'i686-unknown-linux-gnu' |
|
| "i686-unknown-linux-gnu"
|
||||||
'x86_64-unknown-linux-musl' |
|
| "x86_64-unknown-linux-musl"
|
||||||
'x86_64-unknown-linux-gnu' |
|
| "x86_64-unknown-linux-gnu"
|
||||||
'aarch64-unknown-linux-musl' |
|
| "aarch64-unknown-linux-musl"
|
||||||
'aarch64-unknown-linux-gnu' |
|
| "aarch64-unknown-linux-gnu"
|
||||||
'powerpc64le-unknown-linux-musl' |
|
| "powerpc64le-unknown-linux-musl"
|
||||||
'powerpc64le-unknown-linux-gnu';
|
| "powerpc64le-unknown-linux-gnu";
|
||||||
|
|
||||||
// Values are expected to match those used in `process.platform`.
|
// Values are expected to match those used in `process.platform`.
|
||||||
export type Platform = 'darwin' | 'linux' | 'win32';
|
export type Platform = "darwin" | "linux" | "win32";
|
||||||
|
|
||||||
// Values are expected to match those used in `process.arch`.
|
// Values are expected to match those used in `process.arch`.
|
||||||
export type Arch = 'arm64' | 'ia32' | 'x64' | 'ppc64' | 'universal';
|
export type Arch = "arm64" | "ia32" | "x64" | "ppc64" | "universal";
|
||||||
|
|
||||||
// Values are expected to match those used by Visual Studio's `vcvarsall.bat`.
|
// Values are expected to match those used by Visual Studio's `vcvarsall.bat`.
|
||||||
// See https://docs.microsoft.com/cpp/build/building-on-the-command-line?view=msvc-160#vcvarsall-syntax
|
// See https://docs.microsoft.com/cpp/build/building-on-the-command-line?view=msvc-160#vcvarsall-syntax
|
||||||
export type VcVarsArch = 'amd64' | 'arm64' | 'x86';
|
export type VcVarsArch = "amd64" | "arm64" | "x86";
|
||||||
|
|
||||||
export type Target = {
|
export type Target = {
|
||||||
id: TargetId;
|
id: TargetId;
|
||||||
@ -51,140 +51,135 @@ export type Target = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type WindowsTarget = Target & {
|
export type WindowsTarget = Target & {
|
||||||
platform: 'win32';
|
platform: "win32";
|
||||||
vcVarsArch: VcVarsArch;
|
vcVarsArch: VcVarsArch;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LinuxTarget = Target & {
|
export type LinuxTarget = Target & {
|
||||||
platform: 'linux';
|
platform: "linux";
|
||||||
libC: typeof processLibC;
|
libC: typeof processLibC;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UniversalTarget = Target & {
|
export type UniversalTarget = Target & {
|
||||||
arch: 'universal';
|
arch: "universal";
|
||||||
subtargets: Target[];
|
subtargets: Target[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const aarch64AppleDarwin: Target = {
|
const aarch64AppleDarwin: Target = {
|
||||||
id: 'aarch64-apple-darwin',
|
id: "aarch64-apple-darwin",
|
||||||
platform: 'darwin',
|
platform: "darwin",
|
||||||
arch: 'arm64',
|
arch: "arm64",
|
||||||
};
|
};
|
||||||
|
|
||||||
const x8664AppleDarwin: Target = {
|
const x8664AppleDarwin: Target = {
|
||||||
id: 'x86_64-apple-darwin',
|
id: "x86_64-apple-darwin",
|
||||||
platform: 'darwin',
|
platform: "darwin",
|
||||||
arch: 'x64',
|
arch: "x64",
|
||||||
};
|
};
|
||||||
|
|
||||||
const universalAppleDarwin: UniversalTarget = {
|
const universalAppleDarwin: UniversalTarget = {
|
||||||
id: 'universal-apple-darwin',
|
id: "universal-apple-darwin",
|
||||||
platform: 'darwin',
|
platform: "darwin",
|
||||||
arch: 'universal',
|
arch: "universal",
|
||||||
subtargets: [
|
subtargets: [aarch64AppleDarwin, x8664AppleDarwin],
|
||||||
aarch64AppleDarwin,
|
|
||||||
x8664AppleDarwin,
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const i686PcWindowsMsvc: WindowsTarget = {
|
const i686PcWindowsMsvc: WindowsTarget = {
|
||||||
id: 'i686-pc-windows-msvc',
|
id: "i686-pc-windows-msvc",
|
||||||
platform: 'win32',
|
platform: "win32",
|
||||||
arch: 'ia32',
|
arch: "ia32",
|
||||||
vcVarsArch: 'x86',
|
vcVarsArch: "x86",
|
||||||
};
|
};
|
||||||
|
|
||||||
const x8664PcWindowsMsvc: WindowsTarget = {
|
const x8664PcWindowsMsvc: WindowsTarget = {
|
||||||
id: 'x86_64-pc-windows-msvc',
|
id: "x86_64-pc-windows-msvc",
|
||||||
platform: 'win32',
|
platform: "win32",
|
||||||
arch: 'x64',
|
arch: "x64",
|
||||||
vcVarsArch: 'amd64',
|
vcVarsArch: "amd64",
|
||||||
};
|
};
|
||||||
|
|
||||||
const x8664UnknownLinuxGnu: LinuxTarget = {
|
const x8664UnknownLinuxGnu: LinuxTarget = {
|
||||||
id: 'x86_64-unknown-linux-gnu',
|
id: "x86_64-unknown-linux-gnu",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'x64',
|
arch: "x64",
|
||||||
libC: GLIBC,
|
libC: GLIBC,
|
||||||
};
|
};
|
||||||
|
|
||||||
const x8664UnknownLinuxMusl: LinuxTarget = {
|
const x8664UnknownLinuxMusl: LinuxTarget = {
|
||||||
id: 'x86_64-unknown-linux-musl',
|
id: "x86_64-unknown-linux-musl",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'x64',
|
arch: "x64",
|
||||||
libC: MUSL,
|
libC: MUSL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const i686UnknownLinuxGnu: LinuxTarget = {
|
const i686UnknownLinuxGnu: LinuxTarget = {
|
||||||
id: 'i686-unknown-linux-gnu',
|
id: "i686-unknown-linux-gnu",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'ia32',
|
arch: "ia32",
|
||||||
libC: GLIBC,
|
libC: GLIBC,
|
||||||
};
|
};
|
||||||
|
|
||||||
const i686UnknownLinuxMusl: LinuxTarget = {
|
const i686UnknownLinuxMusl: LinuxTarget = {
|
||||||
id: 'i686-unknown-linux-musl',
|
id: "i686-unknown-linux-musl",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'ia32',
|
arch: "ia32",
|
||||||
libC: MUSL,
|
libC: MUSL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const aarch64UnknownLinuxGnu: LinuxTarget = {
|
const aarch64UnknownLinuxGnu: LinuxTarget = {
|
||||||
id: 'aarch64-unknown-linux-gnu',
|
id: "aarch64-unknown-linux-gnu",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'arm64',
|
arch: "arm64",
|
||||||
libC: GLIBC,
|
libC: GLIBC,
|
||||||
};
|
};
|
||||||
|
|
||||||
const aarch64UnknownLinuxMusl: LinuxTarget = {
|
const aarch64UnknownLinuxMusl: LinuxTarget = {
|
||||||
id: 'aarch64-unknown-linux-musl',
|
id: "aarch64-unknown-linux-musl",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'arm64',
|
arch: "arm64",
|
||||||
libC: MUSL,
|
libC: MUSL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const powerpc64leUnknownLinuxGnu: LinuxTarget = {
|
const powerpc64leUnknownLinuxGnu: LinuxTarget = {
|
||||||
id: 'powerpc64le-unknown-linux-gnu',
|
id: "powerpc64le-unknown-linux-gnu",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'ppc64',
|
arch: "ppc64",
|
||||||
libC: GLIBC,
|
libC: GLIBC,
|
||||||
};
|
};
|
||||||
|
|
||||||
const powerpc64leUnknownLinuxMusl: LinuxTarget = {
|
const powerpc64leUnknownLinuxMusl: LinuxTarget = {
|
||||||
id: 'powerpc64le-unknown-linux-musl',
|
id: "powerpc64le-unknown-linux-musl",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'ppc64',
|
arch: "ppc64",
|
||||||
libC: MUSL,
|
libC: MUSL,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TARGETS: Record<TargetId, Target> = {
|
export const TARGETS: Record<TargetId, Target> = {
|
||||||
// macOS
|
// macOS
|
||||||
'aarch64-apple-darwin': aarch64AppleDarwin,
|
"aarch64-apple-darwin": aarch64AppleDarwin,
|
||||||
'x86_64-apple-darwin': x8664AppleDarwin,
|
"x86_64-apple-darwin": x8664AppleDarwin,
|
||||||
'universal-apple-darwin': universalAppleDarwin,
|
"universal-apple-darwin": universalAppleDarwin,
|
||||||
// Windows
|
// Windows
|
||||||
'i686-pc-windows-msvc': i686PcWindowsMsvc,
|
"i686-pc-windows-msvc": i686PcWindowsMsvc,
|
||||||
'x86_64-pc-windows-msvc': x8664PcWindowsMsvc,
|
"x86_64-pc-windows-msvc": x8664PcWindowsMsvc,
|
||||||
// Linux
|
// Linux
|
||||||
'i686-unknown-linux-musl': i686UnknownLinuxMusl,
|
"i686-unknown-linux-musl": i686UnknownLinuxMusl,
|
||||||
'i686-unknown-linux-gnu': i686UnknownLinuxGnu,
|
"i686-unknown-linux-gnu": i686UnknownLinuxGnu,
|
||||||
'x86_64-unknown-linux-musl': x8664UnknownLinuxMusl,
|
"x86_64-unknown-linux-musl": x8664UnknownLinuxMusl,
|
||||||
'x86_64-unknown-linux-gnu': x8664UnknownLinuxGnu,
|
"x86_64-unknown-linux-gnu": x8664UnknownLinuxGnu,
|
||||||
'aarch64-unknown-linux-musl': aarch64UnknownLinuxMusl,
|
"aarch64-unknown-linux-musl": aarch64UnknownLinuxMusl,
|
||||||
'aarch64-unknown-linux-gnu': aarch64UnknownLinuxGnu,
|
"aarch64-unknown-linux-gnu": aarch64UnknownLinuxGnu,
|
||||||
'powerpc64le-unknown-linux-musl': powerpc64leUnknownLinuxMusl,
|
"powerpc64le-unknown-linux-musl": powerpc64leUnknownLinuxMusl,
|
||||||
'powerpc64le-unknown-linux-gnu': powerpc64leUnknownLinuxGnu,
|
"powerpc64le-unknown-linux-gnu": powerpc64leUnknownLinuxGnu,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getHost(): Target | undefined {
|
export function getHost(): Target | undefined {
|
||||||
return Object.values(TARGETS).find(target => (
|
return Object.values(TARGETS).find(
|
||||||
|
(target) =>
|
||||||
target.platform === process.platform &&
|
target.platform === process.platform &&
|
||||||
target.arch === process.arch &&
|
target.arch === process.arch &&
|
||||||
(
|
(process.platform !== "linux" || (target as LinuxTarget).libC === processLibC),
|
||||||
process.platform !== 'linux' ||
|
);
|
||||||
(target as LinuxTarget).libC === processLibC
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isHostId(id: TargetId): boolean {
|
export function isHostId(id: TargetId): boolean {
|
||||||
|
@ -11,31 +11,35 @@ import * as childProcess from "child_process";
|
|||||||
|
|
||||||
export async function versionFromAsar(): Promise<string> {
|
export async function versionFromAsar(): Promise<string> {
|
||||||
try {
|
try {
|
||||||
await fs.stat('webapp.asar');
|
await fs.stat("webapp.asar");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error("No 'webapp.asar' found. Run 'yarn run fetch'");
|
throw new Error("No 'webapp.asar' found. Run 'yarn run fetch'");
|
||||||
}
|
}
|
||||||
|
|
||||||
return asar.extractFile('webapp.asar', 'version').toString().trim();
|
return asar.extractFile("webapp.asar", "version").toString().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setPackageVersion(ver: string): Promise<void> {
|
export async function setPackageVersion(ver: string): Promise<void> {
|
||||||
// set version in package.json: electron-builder will use this to populate
|
// set version in package.json: electron-builder will use this to populate
|
||||||
// all the various version fields
|
// all the various version fields
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
childProcess.execFile(process.platform === 'win32' ? 'yarn.cmd' : 'yarn', [
|
childProcess.execFile(
|
||||||
'version',
|
process.platform === "win32" ? "yarn.cmd" : "yarn",
|
||||||
'-s',
|
[
|
||||||
'--no-git-tag-version', // This also means "don't commit to git" as it turns out
|
"version",
|
||||||
'--new-version',
|
"-s",
|
||||||
|
"--no-git-tag-version", // This also means "don't commit to git" as it turns out
|
||||||
|
"--new-version",
|
||||||
ver,
|
ver,
|
||||||
], (err) => {
|
],
|
||||||
|
(err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,9 +53,11 @@ async function main(args: string[]): Promise<number> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
main(process.argv.slice(2)).then((ret) => {
|
main(process.argv.slice(2))
|
||||||
|
.then((ret) => {
|
||||||
process.exit(ret);
|
process.exit(ret);
|
||||||
}).catch(e => {
|
})
|
||||||
|
.catch((e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
@ -7,14 +7,9 @@
|
|||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"lib": [
|
"lib": ["es2019", "dom"]
|
||||||
"es2019",
|
|
||||||
"dom"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["./**/*.ts"],
|
||||||
"./**/*.ts"
|
|
||||||
],
|
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
"transpileOnly": true
|
"transpileOnly": true
|
||||||
}
|
}
|
||||||
|
2
src/@types/keytar.d.ts
vendored
2
src/@types/keytar.d.ts
vendored
@ -50,5 +50,5 @@ declare module "keytar" {
|
|||||||
*
|
*
|
||||||
* @returns A promise for the array of found credentials.
|
* @returns A promise for the array of found credentials.
|
||||||
*/
|
*/
|
||||||
export function findCredentials(service: string): Promise<Array<{ account: string, password: string}>>;
|
export function findCredentials(service: string): Promise<Array<{ account: string; password: string }>>;
|
||||||
}
|
}
|
||||||
|
@ -19,18 +19,11 @@ limitations under the License.
|
|||||||
|
|
||||||
// Squirrel on windows starts the app with various flags as hooks to tell us when we've been installed/uninstalled etc.
|
// Squirrel on windows starts the app with various flags as hooks to tell us when we've been installed/uninstalled etc.
|
||||||
import "./squirrelhooks";
|
import "./squirrelhooks";
|
||||||
import {
|
import { app, BrowserWindow, Menu, autoUpdater, protocol, dialog } from "electron";
|
||||||
app,
|
|
||||||
BrowserWindow,
|
|
||||||
Menu,
|
|
||||||
autoUpdater,
|
|
||||||
protocol,
|
|
||||||
dialog,
|
|
||||||
} from "electron";
|
|
||||||
import AutoLaunch from "auto-launch";
|
import AutoLaunch from "auto-launch";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import windowStateKeeper from 'electron-window-state';
|
import windowStateKeeper from "electron-window-state";
|
||||||
import Store from 'electron-store';
|
import Store from "electron-store";
|
||||||
import fs, { promises as afs } from "fs";
|
import fs, { promises as afs } from "fs";
|
||||||
import { URL } from "url";
|
import { URL } from "url";
|
||||||
import minimist from "minimist";
|
import minimist from "minimist";
|
||||||
@ -40,11 +33,11 @@ import "./keytar";
|
|||||||
import "./seshat";
|
import "./seshat";
|
||||||
import "./settings";
|
import "./settings";
|
||||||
import * as tray from "./tray";
|
import * as tray from "./tray";
|
||||||
import { buildMenuTemplate } from './vectormenu';
|
import { buildMenuTemplate } from "./vectormenu";
|
||||||
import webContentsHandler from './webcontents-handler';
|
import webContentsHandler from "./webcontents-handler";
|
||||||
import * as updater from './updater';
|
import * as updater from "./updater";
|
||||||
import { getProfileFromDeeplink, protocolInit } from './protocol';
|
import { getProfileFromDeeplink, protocolInit } from "./protocol";
|
||||||
import { _t, AppLocalization } from './language-helper';
|
import { _t, AppLocalization } from "./language-helper";
|
||||||
import Input = Electron.Input;
|
import Input = Electron.Input;
|
||||||
|
|
||||||
const argv = minimist(process.argv, {
|
const argv = minimist(process.argv, {
|
||||||
@ -65,8 +58,7 @@ if (argv["help"]) {
|
|||||||
console.log(" --no-update: Disable automatic updating.");
|
console.log(" --no-update: Disable automatic updating.");
|
||||||
console.log(" --hidden: Start the application hidden in the system tray.");
|
console.log(" --hidden: Start the application hidden in the system tray.");
|
||||||
console.log(" --help: Displays this help message.");
|
console.log(" --help: Displays this help message.");
|
||||||
console.log("And more such as --proxy, see:" +
|
console.log("And more such as --proxy, see:" + "https://electronjs.org/docs/api/command-line-switches");
|
||||||
"https://electronjs.org/docs/api/command-line-switches");
|
|
||||||
app.exit();
|
app.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +66,7 @@ if (argv["help"]) {
|
|||||||
// as soon as the app path is set, so pick a random path in it that must exist if it's a
|
// as soon as the app path is set, so pick a random path in it that must exist if it's a
|
||||||
// real user data directory.
|
// real user data directory.
|
||||||
function isRealUserDataDir(d: string): boolean {
|
function isRealUserDataDir(d: string): boolean {
|
||||||
return fs.existsSync(path.join(d, 'IndexedDB'));
|
return fs.existsSync(path.join(d, "IndexedDB"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we are passed a profile in the SSO callback url
|
// check if we are passed a profile in the SSO callback url
|
||||||
@ -83,22 +75,22 @@ let userDataPath: string;
|
|||||||
const userDataPathInProtocol = getProfileFromDeeplink(argv["_"]);
|
const userDataPathInProtocol = getProfileFromDeeplink(argv["_"]);
|
||||||
if (userDataPathInProtocol) {
|
if (userDataPathInProtocol) {
|
||||||
userDataPath = userDataPathInProtocol;
|
userDataPath = userDataPathInProtocol;
|
||||||
} else if (argv['profile-dir']) {
|
} else if (argv["profile-dir"]) {
|
||||||
userDataPath = argv['profile-dir'];
|
userDataPath = argv["profile-dir"];
|
||||||
} else {
|
} else {
|
||||||
let newUserDataPath = app.getPath('userData');
|
let newUserDataPath = app.getPath("userData");
|
||||||
if (argv['profile']) {
|
if (argv["profile"]) {
|
||||||
newUserDataPath += '-' + argv['profile'];
|
newUserDataPath += "-" + argv["profile"];
|
||||||
}
|
}
|
||||||
const newUserDataPathExists = isRealUserDataDir(newUserDataPath);
|
const newUserDataPathExists = isRealUserDataDir(newUserDataPath);
|
||||||
let oldUserDataPath = path.join(app.getPath('appData'), app.getName().replace('Element', 'Riot'));
|
let oldUserDataPath = path.join(app.getPath("appData"), app.getName().replace("Element", "Riot"));
|
||||||
if (argv['profile']) {
|
if (argv["profile"]) {
|
||||||
oldUserDataPath += '-' + argv['profile'];
|
oldUserDataPath += "-" + argv["profile"];
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldUserDataPathExists = isRealUserDataDir(oldUserDataPath);
|
const oldUserDataPathExists = isRealUserDataDir(oldUserDataPath);
|
||||||
console.log(newUserDataPath + " exists: " + (newUserDataPathExists ? 'yes' : 'no'));
|
console.log(newUserDataPath + " exists: " + (newUserDataPathExists ? "yes" : "no"));
|
||||||
console.log(oldUserDataPath + " exists: " + (oldUserDataPathExists ? 'yes' : 'no'));
|
console.log(oldUserDataPath + " exists: " + (oldUserDataPathExists ? "yes" : "no"));
|
||||||
if (!newUserDataPathExists && oldUserDataPathExists) {
|
if (!newUserDataPathExists && oldUserDataPathExists) {
|
||||||
console.log("Using legacy user data path: " + oldUserDataPath);
|
console.log("Using legacy user data path: " + oldUserDataPath);
|
||||||
userDataPath = oldUserDataPath;
|
userDataPath = oldUserDataPath;
|
||||||
@ -106,54 +98,53 @@ if (userDataPathInProtocol) {
|
|||||||
userDataPath = newUserDataPath;
|
userDataPath = newUserDataPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app.setPath('userData', userDataPath);
|
app.setPath("userData", userDataPath);
|
||||||
|
|
||||||
async function tryPaths(name: string, root: string, rawPaths: string[]): Promise<string> {
|
async function tryPaths(name: string, root: string, rawPaths: string[]): Promise<string> {
|
||||||
// Make everything relative to root
|
// Make everything relative to root
|
||||||
const paths = rawPaths.map(p => path.join(root, p));
|
const paths = rawPaths.map((p) => path.join(root, p));
|
||||||
|
|
||||||
for (const p of paths) {
|
for (const p of paths) {
|
||||||
try {
|
try {
|
||||||
await afs.stat(p);
|
await afs.stat(p);
|
||||||
return p + '/';
|
return p + "/";
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
console.log(`Couldn't find ${name} files in any of: `);
|
console.log(`Couldn't find ${name} files in any of: `);
|
||||||
for (const p of paths) {
|
for (const p of paths) {
|
||||||
console.log("\t"+path.resolve(p));
|
console.log("\t" + path.resolve(p));
|
||||||
}
|
}
|
||||||
throw new Error(`Failed to find ${name} files`);
|
throw new Error(`Failed to find ${name} files`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const homeserverProps = ['default_is_url', 'default_hs_url', 'default_server_name', 'default_server_config'] as const;
|
const homeserverProps = ["default_is_url", "default_hs_url", "default_server_name", "default_server_config"] as const;
|
||||||
|
|
||||||
// Find the webapp resources and set up things that require them
|
// Find the webapp resources and set up things that require them
|
||||||
async function setupGlobals(): Promise<void> {
|
async function setupGlobals(): Promise<void> {
|
||||||
// find the webapp asar.
|
// find the webapp asar.
|
||||||
asarPath = await tryPaths("webapp", __dirname, [
|
asarPath = await tryPaths("webapp", __dirname, [
|
||||||
// If run from the source checkout, this will be in the directory above
|
// If run from the source checkout, this will be in the directory above
|
||||||
'../webapp.asar',
|
"../webapp.asar",
|
||||||
// but if run from a packaged application, electron-main.js will be in
|
// but if run from a packaged application, electron-main.js will be in
|
||||||
// a different asar file so it will be two levels above
|
// a different asar file so it will be two levels above
|
||||||
'../../webapp.asar',
|
"../../webapp.asar",
|
||||||
// also try without the 'asar' suffix to allow symlinking in a directory
|
// also try without the 'asar' suffix to allow symlinking in a directory
|
||||||
'../webapp',
|
"../webapp",
|
||||||
// from a packaged application
|
// from a packaged application
|
||||||
'../../webapp',
|
"../../webapp",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// we assume the resources path is in the same place as the asar
|
// we assume the resources path is in the same place as the asar
|
||||||
resPath = await tryPaths("res", path.dirname(asarPath), [
|
resPath = await tryPaths("res", path.dirname(asarPath), [
|
||||||
// If run from the source checkout
|
// If run from the source checkout
|
||||||
'res',
|
"res",
|
||||||
// if run from packaged application
|
// if run from packaged application
|
||||||
'',
|
"",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
global.vectorConfig = require(asarPath + 'config.json');
|
global.vectorConfig = require(asarPath + "config.json");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// it would be nice to check the error code here and bail if the config
|
// it would be nice to check the error code here and bail if the config
|
||||||
// is unparsable, but we get MODULE_NOT_FOUND in the case of a missing
|
// is unparsable, but we get MODULE_NOT_FOUND in the case of a missing
|
||||||
@ -165,15 +156,15 @@ async function setupGlobals(): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
// Load local config and use it to override values from the one baked with the build
|
// Load local config and use it to override values from the one baked with the build
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const localConfig = require(path.join(app.getPath('userData'), 'config.json'));
|
const localConfig = require(path.join(app.getPath("userData"), "config.json"));
|
||||||
|
|
||||||
// If the local config has a homeserver defined, don't use the homeserver from the build
|
// If the local config has a homeserver defined, don't use the homeserver from the build
|
||||||
// config. This is to avoid a problem where Riot thinks there are multiple homeservers
|
// config. This is to avoid a problem where Riot thinks there are multiple homeservers
|
||||||
// defined, and panics as a result.
|
// defined, and panics as a result.
|
||||||
if (Object.keys(localConfig).find(k => homeserverProps.includes(<any>k))) {
|
if (Object.keys(localConfig).find((k) => homeserverProps.includes(<any>k))) {
|
||||||
// Rip out all the homeserver options from the vector config
|
// Rip out all the homeserver options from the vector config
|
||||||
global.vectorConfig = Object.keys(global.vectorConfig)
|
global.vectorConfig = Object.keys(global.vectorConfig)
|
||||||
.filter(k => !homeserverProps.includes(<any>k))
|
.filter((k) => !homeserverProps.includes(<any>k))
|
||||||
.reduce((obj, key) => {
|
.reduce((obj, key) => {
|
||||||
obj[key] = global.vectorConfig[key];
|
obj[key] = global.vectorConfig[key];
|
||||||
return obj;
|
return obj;
|
||||||
@ -185,9 +176,10 @@ async function setupGlobals(): Promise<void> {
|
|||||||
if (e instanceof SyntaxError) {
|
if (e instanceof SyntaxError) {
|
||||||
dialog.showMessageBox({
|
dialog.showMessageBox({
|
||||||
type: "error",
|
type: "error",
|
||||||
title: `Your ${global.vectorConfig.brand || 'Element'} is misconfigured`,
|
title: `Your ${global.vectorConfig.brand || "Element"} is misconfigured`,
|
||||||
message: `Your custom ${global.vectorConfig.brand || 'Element'} configuration contains invalid JSON. ` +
|
message:
|
||||||
`Please correct the problem and reopen ${global.vectorConfig.brand || 'Element'}.`,
|
`Your custom ${global.vectorConfig.brand || "Element"} configuration contains invalid JSON. ` +
|
||||||
|
`Please correct the problem and reopen ${global.vectorConfig.brand || "Element"}.`,
|
||||||
detail: e.message || "",
|
detail: e.message || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -197,16 +189,16 @@ async function setupGlobals(): Promise<void> {
|
|||||||
|
|
||||||
// The tray icon
|
// The tray icon
|
||||||
// It's important to call `path.join` so we don't end up with the packaged asar in the final path.
|
// It's important to call `path.join` so we don't end up with the packaged asar in the final path.
|
||||||
const iconFile = `element.${process.platform === 'win32' ? 'ico' : 'png'}`;
|
const iconFile = `element.${process.platform === "win32" ? "ico" : "png"}`;
|
||||||
iconPath = path.join(resPath, "img", iconFile);
|
iconPath = path.join(resPath, "img", iconFile);
|
||||||
global.trayConfig = {
|
global.trayConfig = {
|
||||||
icon_path: iconPath,
|
icon_path: iconPath,
|
||||||
brand: global.vectorConfig.brand || 'Element',
|
brand: global.vectorConfig.brand || "Element",
|
||||||
};
|
};
|
||||||
|
|
||||||
// launcher
|
// launcher
|
||||||
global.launcher = new AutoLaunch({
|
global.launcher = new AutoLaunch({
|
||||||
name: global.vectorConfig.brand || 'Element',
|
name: global.vectorConfig.brand || "Element",
|
||||||
isHidden: true,
|
isHidden: true,
|
||||||
mac: {
|
mac: {
|
||||||
useLaunchAgent: true,
|
useLaunchAgent: true,
|
||||||
@ -217,9 +209,9 @@ async function setupGlobals(): Promise<void> {
|
|||||||
async function moveAutoLauncher(): Promise<void> {
|
async function moveAutoLauncher(): Promise<void> {
|
||||||
// Look for an auto-launcher under 'Riot' and if we find one, port it's
|
// Look for an auto-launcher under 'Riot' and if we find one, port it's
|
||||||
// enabled/disabled-ness over to the new 'Element' launcher
|
// enabled/disabled-ness over to the new 'Element' launcher
|
||||||
if (!global.vectorConfig.brand || global.vectorConfig.brand === 'Element') {
|
if (!global.vectorConfig.brand || global.vectorConfig.brand === "Element") {
|
||||||
const oldLauncher = new AutoLaunch({
|
const oldLauncher = new AutoLaunch({
|
||||||
name: 'Riot',
|
name: "Riot",
|
||||||
isHidden: true,
|
isHidden: true,
|
||||||
mac: {
|
mac: {
|
||||||
useLaunchAgent: true,
|
useLaunchAgent: true,
|
||||||
@ -238,22 +230,26 @@ global.store = new Store({ name: "electron-config" });
|
|||||||
global.appQuitting = false;
|
global.appQuitting = false;
|
||||||
|
|
||||||
const exitShortcuts: Array<(input: Input, platform: string) => boolean> = [
|
const exitShortcuts: Array<(input: Input, platform: string) => boolean> = [
|
||||||
(input, platform): boolean => platform !== 'darwin' && input.alt && input.key.toUpperCase() === 'F4',
|
(input, platform): boolean => platform !== "darwin" && input.alt && input.key.toUpperCase() === "F4",
|
||||||
(input, platform): boolean => platform !== 'darwin' && input.control && input.key.toUpperCase() === 'Q',
|
(input, platform): boolean => platform !== "darwin" && input.control && input.key.toUpperCase() === "Q",
|
||||||
(input, platform): boolean => platform === 'darwin' && input.meta && input.key.toUpperCase() === 'Q',
|
(input, platform): boolean => platform === "darwin" && input.meta && input.key.toUpperCase() === "Q",
|
||||||
];
|
];
|
||||||
|
|
||||||
const warnBeforeExit = (event: Event, input: Input): void => {
|
const warnBeforeExit = (event: Event, input: Input): void => {
|
||||||
const shouldWarnBeforeExit = global.store.get('warnBeforeExit', true);
|
const shouldWarnBeforeExit = global.store.get("warnBeforeExit", true);
|
||||||
const exitShortcutPressed =
|
const exitShortcutPressed =
|
||||||
input.type === 'keyDown' && exitShortcuts.some(shortcutFn => shortcutFn(input, process.platform));
|
input.type === "keyDown" && exitShortcuts.some((shortcutFn) => shortcutFn(input, process.platform));
|
||||||
|
|
||||||
if (shouldWarnBeforeExit && exitShortcutPressed && global.mainWindow) {
|
if (shouldWarnBeforeExit && exitShortcutPressed && global.mainWindow) {
|
||||||
const shouldCancelCloseRequest = dialog.showMessageBoxSync(global.mainWindow, {
|
const shouldCancelCloseRequest =
|
||||||
|
dialog.showMessageBoxSync(global.mainWindow, {
|
||||||
type: "question",
|
type: "question",
|
||||||
buttons: [_t("Cancel"), _t("Close %(brand)s", {
|
buttons: [
|
||||||
brand: global.vectorConfig.brand || 'Element',
|
_t("Cancel"),
|
||||||
})],
|
_t("Close %(brand)s", {
|
||||||
|
brand: global.vectorConfig.brand || "Element",
|
||||||
|
}),
|
||||||
|
],
|
||||||
message: _t("Are you sure you want to quit?"),
|
message: _t("Are you sure you want to quit?"),
|
||||||
defaultId: 1,
|
defaultId: 1,
|
||||||
cancelId: 0,
|
cancelId: 0,
|
||||||
@ -271,18 +267,18 @@ const warnBeforeExit = (event: Event, input: Input): void => {
|
|||||||
// no other way to catch this error).
|
// no other way to catch this error).
|
||||||
// Assuming we generally run from the console when developing,
|
// Assuming we generally run from the console when developing,
|
||||||
// this is far preferable.
|
// this is far preferable.
|
||||||
process.on('uncaughtException', function(error: Error): void {
|
process.on("uncaughtException", function (error: Error): void {
|
||||||
console.log('Unhandled exception', error);
|
console.log("Unhandled exception", error);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.commandLine.appendSwitch('--enable-usermedia-screen-capturing');
|
app.commandLine.appendSwitch("--enable-usermedia-screen-capturing");
|
||||||
if (!app.commandLine.hasSwitch('enable-features')) {
|
if (!app.commandLine.hasSwitch("enable-features")) {
|
||||||
app.commandLine.appendSwitch('enable-features', 'WebRTCPipeWireCapturer');
|
app.commandLine.appendSwitch("enable-features", "WebRTCPipeWireCapturer");
|
||||||
}
|
}
|
||||||
|
|
||||||
const gotLock = app.requestSingleInstanceLock();
|
const gotLock = app.requestSingleInstanceLock();
|
||||||
if (!gotLock) {
|
if (!gotLock) {
|
||||||
console.log('Other instance detected: exiting');
|
console.log("Other instance detected: exiting");
|
||||||
app.exit();
|
app.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,14 +290,16 @@ protocolInit();
|
|||||||
// work.
|
// work.
|
||||||
// Also mark it as secure (ie. accessing resources from this
|
// Also mark it as secure (ie. accessing resources from this
|
||||||
// protocol and HTTPS won't trigger mixed content warnings).
|
// protocol and HTTPS won't trigger mixed content warnings).
|
||||||
protocol.registerSchemesAsPrivileged([{
|
protocol.registerSchemesAsPrivileged([
|
||||||
scheme: 'vector',
|
{
|
||||||
|
scheme: "vector",
|
||||||
privileges: {
|
privileges: {
|
||||||
standard: true,
|
standard: true,
|
||||||
secure: true,
|
secure: true,
|
||||||
supportFetchAPI: true,
|
supportFetchAPI: true,
|
||||||
},
|
},
|
||||||
}]);
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
// Turn the sandbox on for *all* windows we might generate. Doing this means we don't
|
// Turn the sandbox on for *all* windows we might generate. Doing this means we don't
|
||||||
// have to specify a `sandbox: true` to each BrowserWindow.
|
// have to specify a `sandbox: true` to each BrowserWindow.
|
||||||
@ -315,15 +313,15 @@ protocol.registerSchemesAsPrivileged([{
|
|||||||
app.enableSandbox();
|
app.enableSandbox();
|
||||||
|
|
||||||
// We disable media controls here. We do this because calls use audio and video elements and they sometimes capture the media keys. See https://github.com/vector-im/element-web/issues/15704
|
// We disable media controls here. We do this because calls use audio and video elements and they sometimes capture the media keys. See https://github.com/vector-im/element-web/issues/15704
|
||||||
app.commandLine.appendSwitch('disable-features', 'HardwareMediaKeyHandling,MediaSessionService');
|
app.commandLine.appendSwitch("disable-features", "HardwareMediaKeyHandling,MediaSessionService");
|
||||||
|
|
||||||
// Disable hardware acceleration if the setting has been set.
|
// Disable hardware acceleration if the setting has been set.
|
||||||
if (global.store.get('disableHardwareAcceleration', false) === true) {
|
if (global.store.get("disableHardwareAcceleration", false) === true) {
|
||||||
console.log("Disabling hardware acceleration.");
|
console.log("Disabling hardware acceleration.");
|
||||||
app.disableHardwareAcceleration();
|
app.disableHardwareAcceleration();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.on('ready', async () => {
|
app.on("ready", async () => {
|
||||||
try {
|
try {
|
||||||
await setupGlobals();
|
await setupGlobals();
|
||||||
await moveAutoLauncher();
|
await moveAutoLauncher();
|
||||||
@ -337,51 +335,51 @@ app.on('ready', async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argv['devtools']) {
|
if (argv["devtools"]) {
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const { default: installExt, REACT_DEVELOPER_TOOLS, REACT_PERF } = require('electron-devtools-installer');
|
const { default: installExt, REACT_DEVELOPER_TOOLS, REACT_PERF } = require("electron-devtools-installer");
|
||||||
installExt(REACT_DEVELOPER_TOOLS)
|
installExt(REACT_DEVELOPER_TOOLS)
|
||||||
.then((name: string) => console.log(`Added Extension: ${name}`))
|
.then((name: string) => console.log(`Added Extension: ${name}`))
|
||||||
.catch((err: unknown) => console.log('An error occurred: ', err));
|
.catch((err: unknown) => console.log("An error occurred: ", err));
|
||||||
installExt(REACT_PERF)
|
installExt(REACT_PERF)
|
||||||
.then((name: string) => console.log(`Added Extension: ${name}`))
|
.then((name: string) => console.log(`Added Extension: ${name}`))
|
||||||
.catch((err: unknown) => console.log('An error occurred: ', err));
|
.catch((err: unknown) => console.log("An error occurred: ", err));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol.registerFileProtocol('vector', (request, callback) => {
|
protocol.registerFileProtocol("vector", (request, callback) => {
|
||||||
if (request.method !== 'GET') {
|
if (request.method !== "GET") {
|
||||||
callback({ error: -322 }); // METHOD_NOT_SUPPORTED from chromium/src/net/base/net_error_list.h
|
callback({ error: -322 }); // METHOD_NOT_SUPPORTED from chromium/src/net/base/net_error_list.h
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedUrl = new URL(request.url);
|
const parsedUrl = new URL(request.url);
|
||||||
if (parsedUrl.protocol !== 'vector:') {
|
if (parsedUrl.protocol !== "vector:") {
|
||||||
callback({ error: -302 }); // UNKNOWN_URL_SCHEME
|
callback({ error: -302 }); // UNKNOWN_URL_SCHEME
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (parsedUrl.host !== 'vector') {
|
if (parsedUrl.host !== "vector") {
|
||||||
callback({ error: -105 }); // NAME_NOT_RESOLVED
|
callback({ error: -105 }); // NAME_NOT_RESOLVED
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = parsedUrl.pathname.split('/');
|
const target = parsedUrl.pathname.split("/");
|
||||||
|
|
||||||
// path starts with a '/'
|
// path starts with a '/'
|
||||||
if (target[0] !== '') {
|
if (target[0] !== "") {
|
||||||
callback({ error: -6 }); // FILE_NOT_FOUND
|
callback({ error: -6 }); // FILE_NOT_FOUND
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target[target.length - 1] == '') {
|
if (target[target.length - 1] == "") {
|
||||||
target[target.length - 1] = 'index.html';
|
target[target.length - 1] = "index.html";
|
||||||
}
|
}
|
||||||
|
|
||||||
let baseDir: string;
|
let baseDir: string;
|
||||||
if (target[1] === 'webapp') {
|
if (target[1] === "webapp") {
|
||||||
baseDir = asarPath;
|
baseDir = asarPath;
|
||||||
} else {
|
} else {
|
||||||
callback({ error: -6 }); // FILE_NOT_FOUND
|
callback({ error: -6 }); // FILE_NOT_FOUND
|
||||||
@ -393,7 +391,7 @@ app.on('ready', async () => {
|
|||||||
baseDir = path.normalize(baseDir);
|
baseDir = path.normalize(baseDir);
|
||||||
|
|
||||||
const relTarget = path.normalize(path.join(...target.slice(2)));
|
const relTarget = path.normalize(path.join(...target.slice(2)));
|
||||||
if (relTarget.startsWith('..')) {
|
if (relTarget.startsWith("..")) {
|
||||||
callback({ error: -6 }); // FILE_NOT_FOUND
|
callback({ error: -6 }); // FILE_NOT_FOUND
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -404,13 +402,13 @@ app.on('ready', async () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (argv['no-update']) {
|
if (argv["no-update"]) {
|
||||||
console.log('Auto update disabled via command line flag "--no-update"');
|
console.log('Auto update disabled via command line flag "--no-update"');
|
||||||
} else if (global.vectorConfig['update_base_url']) {
|
} else if (global.vectorConfig["update_base_url"]) {
|
||||||
console.log(`Starting auto update with base URL: ${global.vectorConfig['update_base_url']}`);
|
console.log(`Starting auto update with base URL: ${global.vectorConfig["update_base_url"]}`);
|
||||||
updater.start(global.vectorConfig['update_base_url']);
|
updater.start(global.vectorConfig["update_base_url"]);
|
||||||
} else {
|
} else {
|
||||||
console.log('No update_base_url is defined: auto update is disabled');
|
console.log("No update_base_url is defined: auto update is disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the previous window state with fallback to defaults
|
// Load the previous window state with fallback to defaults
|
||||||
@ -422,11 +420,11 @@ app.on('ready', async () => {
|
|||||||
const preloadScript = path.normalize(`${__dirname}/preload.js`);
|
const preloadScript = path.normalize(`${__dirname}/preload.js`);
|
||||||
global.mainWindow = new BrowserWindow({
|
global.mainWindow = new BrowserWindow({
|
||||||
// https://www.electronjs.org/docs/faq#the-font-looks-blurry-what-is-this-and-what-can-i-do
|
// https://www.electronjs.org/docs/faq#the-font-looks-blurry-what-is-this-and-what-can-i-do
|
||||||
backgroundColor: '#fff',
|
backgroundColor: "#fff",
|
||||||
|
|
||||||
icon: iconPath,
|
icon: iconPath,
|
||||||
show: false,
|
show: false,
|
||||||
autoHideMenuBar: global.store.get('autoHideMenuBar', true),
|
autoHideMenuBar: global.store.get("autoHideMenuBar", true),
|
||||||
|
|
||||||
x: mainWindowState.x,
|
x: mainWindowState.x,
|
||||||
y: mainWindowState.y,
|
y: mainWindowState.y,
|
||||||
@ -440,20 +438,20 @@ app.on('ready', async () => {
|
|||||||
webgl: true,
|
webgl: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
global.mainWindow.loadURL('vector://vector/webapp/');
|
global.mainWindow.loadURL("vector://vector/webapp/");
|
||||||
|
|
||||||
// Handle spellchecker
|
// Handle spellchecker
|
||||||
// For some reason spellCheckerEnabled isn't persisted, so we have to use the store here
|
// For some reason spellCheckerEnabled isn't persisted, so we have to use the store here
|
||||||
global.mainWindow.webContents.session.setSpellCheckerEnabled(global.store.get("spellCheckerEnabled", true));
|
global.mainWindow.webContents.session.setSpellCheckerEnabled(global.store.get("spellCheckerEnabled", true));
|
||||||
|
|
||||||
// Create trayIcon icon
|
// Create trayIcon icon
|
||||||
if (global.store.get('minimizeToTray', true)) tray.create(global.trayConfig);
|
if (global.store.get("minimizeToTray", true)) tray.create(global.trayConfig);
|
||||||
|
|
||||||
global.mainWindow.once('ready-to-show', () => {
|
global.mainWindow.once("ready-to-show", () => {
|
||||||
if (!global.mainWindow) return;
|
if (!global.mainWindow) return;
|
||||||
mainWindowState.manage(global.mainWindow);
|
mainWindowState.manage(global.mainWindow);
|
||||||
|
|
||||||
if (!argv['hidden']) {
|
if (!argv["hidden"]) {
|
||||||
global.mainWindow.show();
|
global.mainWindow.show();
|
||||||
} else {
|
} else {
|
||||||
// hide here explicitly because window manage above sometimes shows it
|
// hide here explicitly because window manage above sometimes shows it
|
||||||
@ -461,21 +459,21 @@ app.on('ready', async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
global.mainWindow.webContents.on('before-input-event', warnBeforeExit);
|
global.mainWindow.webContents.on("before-input-event", warnBeforeExit);
|
||||||
|
|
||||||
global.mainWindow.on('closed', () => {
|
global.mainWindow.on("closed", () => {
|
||||||
global.mainWindow = null;
|
global.mainWindow = null;
|
||||||
});
|
});
|
||||||
global.mainWindow.on('close', async (e) => {
|
global.mainWindow.on("close", async (e) => {
|
||||||
// If we are not quitting and have a tray icon then minimize to tray
|
// If we are not quitting and have a tray icon then minimize to tray
|
||||||
if (!global.appQuitting && (tray.hasTray() || process.platform === 'darwin')) {
|
if (!global.appQuitting && (tray.hasTray() || process.platform === "darwin")) {
|
||||||
// On Mac, closing the window just hides it
|
// On Mac, closing the window just hides it
|
||||||
// (this is generally how single-window Mac apps
|
// (this is generally how single-window Mac apps
|
||||||
// behave, eg. Mail.app)
|
// behave, eg. Mail.app)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (global.mainWindow?.isFullScreen()) {
|
if (global.mainWindow?.isFullScreen()) {
|
||||||
global.mainWindow.once('leave-full-screen', () => global.mainWindow?.hide());
|
global.mainWindow.once("leave-full-screen", () => global.mainWindow?.hide());
|
||||||
|
|
||||||
global.mainWindow.setFullScreen(false);
|
global.mainWindow.setFullScreen(false);
|
||||||
} else {
|
} else {
|
||||||
@ -486,12 +484,12 @@ app.on('ready', async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === "win32") {
|
||||||
// Handle forward/backward mouse buttons in Windows
|
// Handle forward/backward mouse buttons in Windows
|
||||||
global.mainWindow.on('app-command', (e, cmd) => {
|
global.mainWindow.on("app-command", (e, cmd) => {
|
||||||
if (cmd === 'browser-backward' && global.mainWindow?.webContents.canGoBack()) {
|
if (cmd === "browser-backward" && global.mainWindow?.webContents.canGoBack()) {
|
||||||
global.mainWindow.webContents.goBack();
|
global.mainWindow.webContents.goBack();
|
||||||
} else if (cmd === 'browser-forward' && global.mainWindow?.webContents.canGoForward()) {
|
} else if (cmd === "browser-forward" && global.mainWindow?.webContents.canGoForward()) {
|
||||||
global.mainWindow.webContents.goForward();
|
global.mainWindow.webContents.goForward();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -501,32 +499,29 @@ app.on('ready', async () => {
|
|||||||
|
|
||||||
global.appLocalization = new AppLocalization({
|
global.appLocalization = new AppLocalization({
|
||||||
store: global.store,
|
store: global.store,
|
||||||
components: [
|
components: [(): void => tray.initApplicationMenu(), (): void => Menu.setApplicationMenu(buildMenuTemplate())],
|
||||||
(): void => tray.initApplicationMenu(),
|
|
||||||
(): void => Menu.setApplicationMenu(buildMenuTemplate()),
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
app.on("window-all-closed", () => {
|
||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('activate', () => {
|
app.on("activate", () => {
|
||||||
global.mainWindow?.show();
|
global.mainWindow?.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
function beforeQuit(): void {
|
function beforeQuit(): void {
|
||||||
global.appQuitting = true;
|
global.appQuitting = true;
|
||||||
global.mainWindow?.webContents.send('before-quit');
|
global.mainWindow?.webContents.send("before-quit");
|
||||||
}
|
}
|
||||||
|
|
||||||
app.on('before-quit', beforeQuit);
|
app.on("before-quit", beforeQuit);
|
||||||
autoUpdater.on('before-quit-for-update', beforeQuit);
|
autoUpdater.on("before-quit-for-update", beforeQuit);
|
||||||
|
|
||||||
app.on('second-instance', (ev, commandLine, workingDirectory) => {
|
app.on("second-instance", (ev, commandLine, workingDirectory) => {
|
||||||
// If other instance launched with --hidden then skip showing window
|
// If other instance launched with --hidden then skip showing window
|
||||||
if (commandLine.includes('--hidden')) return;
|
if (commandLine.includes("--hidden")) return;
|
||||||
|
|
||||||
// Someone tried to run a second instance, we should focus our window.
|
// Someone tried to run a second instance, we should focus our window.
|
||||||
if (global.mainWindow) {
|
if (global.mainWindow) {
|
||||||
@ -540,4 +535,4 @@ app.on('second-instance', (ev, commandLine, workingDirectory) => {
|
|||||||
// installer uses for the shortcut icon.
|
// installer uses for the shortcut icon.
|
||||||
// This makes notifications work on windows 8.1 (and is
|
// This makes notifications work on windows 8.1 (and is
|
||||||
// a noop on other platforms).
|
// a noop on other platforms).
|
||||||
app.setAppUserModelId('com.squirrel.element-desktop.Element');
|
app.setAppUserModelId("com.squirrel.element-desktop.Element");
|
||||||
|
67
src/ipc.ts
67
src/ipc.ts
@ -22,8 +22,8 @@ import { randomArray } from "./utils";
|
|||||||
import { Settings } from "./settings";
|
import { Settings } from "./settings";
|
||||||
import { keytar } from "./keytar";
|
import { keytar } from "./keytar";
|
||||||
|
|
||||||
ipcMain.on('setBadgeCount', function(_ev: IpcMainEvent, count: number): void {
|
ipcMain.on("setBadgeCount", function (_ev: IpcMainEvent, count: number): void {
|
||||||
if (process.platform !== 'win32') {
|
if (process.platform !== "win32") {
|
||||||
// only set badgeCount on Mac/Linux, the docs say that only those platforms support it but turns out Electron
|
// only set badgeCount on Mac/Linux, the docs say that only those platforms support it but turns out Electron
|
||||||
// has some Windows support too, and in some Windows environments this leads to two badges rendering atop
|
// has some Windows support too, and in some Windows environments this leads to two badges rendering atop
|
||||||
// each other. See https://github.com/vector-im/element-web/issues/16942
|
// each other. See https://github.com/vector-im/element-web/issues/16942
|
||||||
@ -35,10 +35,10 @@ ipcMain.on('setBadgeCount', function(_ev: IpcMainEvent, count: number): void {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let focusHandlerAttached = false;
|
let focusHandlerAttached = false;
|
||||||
ipcMain.on('loudNotification', function(): void {
|
ipcMain.on("loudNotification", function (): void {
|
||||||
if (process.platform === 'win32' && global.mainWindow && !global.mainWindow.isFocused() && !focusHandlerAttached) {
|
if (process.platform === "win32" && global.mainWindow && !global.mainWindow.isFocused() && !focusHandlerAttached) {
|
||||||
global.mainWindow.flashFrame(true);
|
global.mainWindow.flashFrame(true);
|
||||||
global.mainWindow.once('focus', () => {
|
global.mainWindow.once("focus", () => {
|
||||||
global.mainWindow?.flashFrame(false);
|
global.mainWindow?.flashFrame(false);
|
||||||
focusHandlerAttached = false;
|
focusHandlerAttached = false;
|
||||||
});
|
});
|
||||||
@ -47,17 +47,17 @@ ipcMain.on('loudNotification', function(): void {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let powerSaveBlockerId: number | null = null;
|
let powerSaveBlockerId: number | null = null;
|
||||||
ipcMain.on('app_onAction', function(_ev: IpcMainEvent, payload) {
|
ipcMain.on("app_onAction", function (_ev: IpcMainEvent, payload) {
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
case 'call_state': {
|
case "call_state": {
|
||||||
if (powerSaveBlockerId !== null && powerSaveBlocker.isStarted(powerSaveBlockerId)) {
|
if (powerSaveBlockerId !== null && powerSaveBlocker.isStarted(powerSaveBlockerId)) {
|
||||||
if (payload.state === 'ended') {
|
if (payload.state === "ended") {
|
||||||
powerSaveBlocker.stop(powerSaveBlockerId);
|
powerSaveBlocker.stop(powerSaveBlockerId);
|
||||||
powerSaveBlockerId = null;
|
powerSaveBlockerId = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (powerSaveBlockerId === null && payload.state === 'connected') {
|
if (powerSaveBlockerId === null && payload.state === "connected") {
|
||||||
powerSaveBlockerId = powerSaveBlocker.start('prevent-display-sleep');
|
powerSaveBlockerId = powerSaveBlocker.start("prevent-display-sleep");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -65,35 +65,35 @@ ipcMain.on('app_onAction', function(_ev: IpcMainEvent, payload) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('ipcCall', async function(_ev: IpcMainEvent, payload) {
|
ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) {
|
||||||
if (!global.mainWindow) return;
|
if (!global.mainWindow) return;
|
||||||
|
|
||||||
const args = payload.args || [];
|
const args = payload.args || [];
|
||||||
let ret: any;
|
let ret: any;
|
||||||
|
|
||||||
switch (payload.name) {
|
switch (payload.name) {
|
||||||
case 'getUpdateFeedUrl':
|
case "getUpdateFeedUrl":
|
||||||
ret = autoUpdater.getFeedURL();
|
ret = autoUpdater.getFeedURL();
|
||||||
break;
|
break;
|
||||||
case 'getSettingValue': {
|
case "getSettingValue": {
|
||||||
const [settingName] = args;
|
const [settingName] = args;
|
||||||
const setting = Settings[settingName];
|
const setting = Settings[settingName];
|
||||||
ret = await setting.read();
|
ret = await setting.read();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'setSettingValue': {
|
case "setSettingValue": {
|
||||||
const [settingName, value] = args;
|
const [settingName, value] = args;
|
||||||
const setting = Settings[settingName];
|
const setting = Settings[settingName];
|
||||||
await setting.write(value);
|
await setting.write(value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'setLanguage':
|
case "setLanguage":
|
||||||
global.appLocalization.setAppLocale(args[0]);
|
global.appLocalization.setAppLocale(args[0]);
|
||||||
break;
|
break;
|
||||||
case 'getAppVersion':
|
case "getAppVersion":
|
||||||
ret = app.getVersion();
|
ret = app.getVersion();
|
||||||
break;
|
break;
|
||||||
case 'focusWindow':
|
case "focusWindow":
|
||||||
if (global.mainWindow.isMinimized()) {
|
if (global.mainWindow.isMinimized()) {
|
||||||
global.mainWindow.restore();
|
global.mainWindow.restore();
|
||||||
} else if (!global.mainWindow.isVisible()) {
|
} else if (!global.mainWindow.isVisible()) {
|
||||||
@ -102,31 +102,31 @@ ipcMain.on('ipcCall', async function(_ev: IpcMainEvent, payload) {
|
|||||||
global.mainWindow.focus();
|
global.mainWindow.focus();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'getConfig':
|
case "getConfig":
|
||||||
ret = global.vectorConfig;
|
ret = global.vectorConfig;
|
||||||
break;
|
break;
|
||||||
case 'navigateBack':
|
case "navigateBack":
|
||||||
if (global.mainWindow.webContents.canGoBack()) {
|
if (global.mainWindow.webContents.canGoBack()) {
|
||||||
global.mainWindow.webContents.goBack();
|
global.mainWindow.webContents.goBack();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'navigateForward':
|
case "navigateForward":
|
||||||
if (global.mainWindow.webContents.canGoForward()) {
|
if (global.mainWindow.webContents.canGoForward()) {
|
||||||
global.mainWindow.webContents.goForward();
|
global.mainWindow.webContents.goForward();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'setSpellCheckEnabled':
|
case "setSpellCheckEnabled":
|
||||||
if (typeof args[0] !== 'boolean') return;
|
if (typeof args[0] !== "boolean") return;
|
||||||
|
|
||||||
global.mainWindow.webContents.session.setSpellCheckerEnabled(args[0]);
|
global.mainWindow.webContents.session.setSpellCheckerEnabled(args[0]);
|
||||||
global.store.set("spellCheckerEnabled", args[0]);
|
global.store.set("spellCheckerEnabled", args[0]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'getSpellCheckEnabled':
|
case "getSpellCheckEnabled":
|
||||||
ret = global.store.get("spellCheckerEnabled", true);
|
ret = global.store.get("spellCheckerEnabled", true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'setSpellCheckLanguages':
|
case "setSpellCheckLanguages":
|
||||||
try {
|
try {
|
||||||
global.mainWindow.webContents.session.setSpellCheckerLanguages(args[0]);
|
global.mainWindow.webContents.session.setSpellCheckerLanguages(args[0]);
|
||||||
} catch (er) {
|
} catch (er) {
|
||||||
@ -134,18 +134,18 @@ ipcMain.on('ipcCall', async function(_ev: IpcMainEvent, payload) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'getSpellCheckLanguages':
|
case "getSpellCheckLanguages":
|
||||||
ret = global.mainWindow.webContents.session.getSpellCheckerLanguages();
|
ret = global.mainWindow.webContents.session.getSpellCheckerLanguages();
|
||||||
break;
|
break;
|
||||||
case 'getAvailableSpellCheckLanguages':
|
case "getAvailableSpellCheckLanguages":
|
||||||
ret = global.mainWindow.webContents.session.availableSpellCheckerLanguages;
|
ret = global.mainWindow.webContents.session.availableSpellCheckerLanguages;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'startSSOFlow':
|
case "startSSOFlow":
|
||||||
recordSSOSession(args[0]);
|
recordSSOSession(args[0]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'getPickleKey':
|
case "getPickleKey":
|
||||||
try {
|
try {
|
||||||
ret = await keytar?.getPassword("element.io", `${args[0]}|${args[1]}`);
|
ret = await keytar?.getPassword("element.io", `${args[0]}|${args[1]}`);
|
||||||
// migrate from riot.im (remove once we think there will no longer be
|
// migrate from riot.im (remove once we think there will no longer be
|
||||||
@ -160,7 +160,7 @@ ipcMain.on('ipcCall', async function(_ev: IpcMainEvent, payload) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'createPickleKey':
|
case "createPickleKey":
|
||||||
try {
|
try {
|
||||||
const pickleKey = await randomArray(32);
|
const pickleKey = await randomArray(32);
|
||||||
await keytar?.setPassword("element.io", `${args[0]}|${args[1]}`, pickleKey);
|
await keytar?.setPassword("element.io", `${args[0]}|${args[1]}`, pickleKey);
|
||||||
@ -170,7 +170,7 @@ ipcMain.on('ipcCall', async function(_ev: IpcMainEvent, payload) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'destroyPickleKey':
|
case "destroyPickleKey":
|
||||||
try {
|
try {
|
||||||
await keytar?.deletePassword("element.io", `${args[0]}|${args[1]}`);
|
await keytar?.deletePassword("element.io", `${args[0]}|${args[1]}`);
|
||||||
// migrate from riot.im (remove once we think there will no longer be
|
// migrate from riot.im (remove once we think there will no longer be
|
||||||
@ -178,7 +178,7 @@ ipcMain.on('ipcCall', async function(_ev: IpcMainEvent, payload) {
|
|||||||
await keytar?.deletePassword("riot.im", `${args[0]}|${args[1]}`);
|
await keytar?.deletePassword("riot.im", `${args[0]}|${args[1]}`);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
break;
|
break;
|
||||||
case 'getDesktopCapturerSources':
|
case "getDesktopCapturerSources":
|
||||||
ret = (await desktopCapturer.getSources(args[0])).map((source) => ({
|
ret = (await desktopCapturer.getSources(args[0])).map((source) => ({
|
||||||
id: source.id,
|
id: source.id,
|
||||||
name: source.name,
|
name: source.name,
|
||||||
@ -187,16 +187,15 @@ ipcMain.on('ipcCall', async function(_ev: IpcMainEvent, payload) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
global.mainWindow.webContents.send('ipcReply', {
|
global.mainWindow.webContents.send("ipcReply", {
|
||||||
id: payload.id,
|
id: payload.id,
|
||||||
error: "Unknown IPC Call: " + payload.name,
|
error: "Unknown IPC Call: " + payload.name,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
global.mainWindow.webContents.send('ipcReply', {
|
global.mainWindow.webContents.send("ipcReply", {
|
||||||
id: payload.id,
|
id: payload.id,
|
||||||
reply: ret,
|
reply: ret,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ import type * as Keytar from "keytar"; // Hak dependency type
|
|||||||
let keytar: typeof Keytar | undefined;
|
let keytar: typeof Keytar | undefined;
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
keytar = require('keytar');
|
keytar = require("keytar");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if ((<NodeJS.ErrnoException>e).code === "MODULE_NOT_FOUND") {
|
if ((<NodeJS.ErrnoException>e).code === "MODULE_NOT_FOUND") {
|
||||||
console.log("Keytar isn't installed; secure key storage is disabled.");
|
console.log("Keytar isn't installed; secure key storage is disabled.");
|
||||||
|
@ -16,9 +16,9 @@ limitations under the License.
|
|||||||
|
|
||||||
import counterpart from "counterpart";
|
import counterpart from "counterpart";
|
||||||
|
|
||||||
import type Store from 'electron-store';
|
import type Store from "electron-store";
|
||||||
|
|
||||||
const FALLBACK_LOCALE = 'en';
|
const FALLBACK_LOCALE = "en";
|
||||||
|
|
||||||
export function _td(text: string): string {
|
export function _td(text: string): string {
|
||||||
return text;
|
return text;
|
||||||
@ -44,11 +44,11 @@ export function _t(text: string, variables: IVariables = {}): string {
|
|||||||
Object.keys(variables).forEach((key) => {
|
Object.keys(variables).forEach((key) => {
|
||||||
if (variables[key] === undefined) {
|
if (variables[key] === undefined) {
|
||||||
console.warn("safeCounterpartTranslate called with undefined interpolation name: " + key);
|
console.warn("safeCounterpartTranslate called with undefined interpolation name: " + key);
|
||||||
variables[key] = 'undefined';
|
variables[key] = "undefined";
|
||||||
}
|
}
|
||||||
if (variables[key] === null) {
|
if (variables[key] === null) {
|
||||||
console.warn("safeCounterpartTranslate called with null interpolation name: " + key);
|
console.warn("safeCounterpartTranslate called with null interpolation name: " + key);
|
||||||
variables[key] = 'null';
|
variables[key] = "null";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let translated = counterpart.translate(text, variables);
|
let translated = counterpart.translate(text, variables);
|
||||||
@ -71,10 +71,10 @@ export class AppLocalization {
|
|||||||
private readonly store: TypedStore;
|
private readonly store: TypedStore;
|
||||||
private readonly localizedComponents?: Set<Component>;
|
private readonly localizedComponents?: Set<Component>;
|
||||||
|
|
||||||
public constructor({ store, components = [] }: { store: TypedStore, components: Component[] }) {
|
public constructor({ store, components = [] }: { store: TypedStore; components: Component[] }) {
|
||||||
counterpart.registerTranslations(FALLBACK_LOCALE, this.fetchTranslationJson("en_EN"));
|
counterpart.registerTranslations(FALLBACK_LOCALE, this.fetchTranslationJson("en_EN"));
|
||||||
counterpart.setFallbackLocale(FALLBACK_LOCALE);
|
counterpart.setFallbackLocale(FALLBACK_LOCALE);
|
||||||
counterpart.setSeparator('|');
|
counterpart.setSeparator("|");
|
||||||
|
|
||||||
if (Array.isArray(components)) {
|
if (Array.isArray(components)) {
|
||||||
this.localizedComponents = new Set(components);
|
this.localizedComponents = new Set(components);
|
||||||
@ -119,7 +119,7 @@ export class AppLocalization {
|
|||||||
locales = [locales];
|
locales = [locales];
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadedLocales = locales.filter(locale => {
|
const loadedLocales = locales.filter((locale) => {
|
||||||
const translations = this.fetchTranslationJson(locale);
|
const translations = this.fetchTranslationJson(locale);
|
||||||
if (translations !== null) {
|
if (translations !== null) {
|
||||||
counterpart.registerTranslations(locale, translations);
|
counterpart.registerTranslations(locale, translations);
|
||||||
@ -135,7 +135,7 @@ export class AppLocalization {
|
|||||||
|
|
||||||
public resetLocalizedUI(): void {
|
public resetLocalizedUI(): void {
|
||||||
console.log("Resetting the UI components after locale change");
|
console.log("Resetting the UI components after locale change");
|
||||||
this.localizedComponents?.forEach(componentSetup => {
|
this.localizedComponents?.forEach((componentSetup) => {
|
||||||
if (typeof componentSetup === "function") {
|
if (typeof componentSetup === "function") {
|
||||||
componentSetup();
|
componentSetup();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ipcRenderer, contextBridge, IpcRendererEvent } from 'electron';
|
import { ipcRenderer, contextBridge, IpcRendererEvent } from "electron";
|
||||||
|
|
||||||
// Expose only expected IPC wrapper APIs to the renderer process to avoid
|
// Expose only expected IPC wrapper APIs to the renderer process to avoid
|
||||||
// handing out generalised messaging access.
|
// handing out generalised messaging access.
|
||||||
@ -36,9 +36,7 @@ const CHANNELS = [
|
|||||||
"userDownloadAction",
|
"userDownloadAction",
|
||||||
];
|
];
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld(
|
contextBridge.exposeInMainWorld("electron", {
|
||||||
"electron",
|
|
||||||
{
|
|
||||||
on(channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => void): void {
|
on(channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => void): void {
|
||||||
if (!CHANNELS.includes(channel)) {
|
if (!CHANNELS.includes(channel)) {
|
||||||
console.error(`Unknown IPC channel ${channel} ignored`);
|
console.error(`Unknown IPC channel ${channel} ignored`);
|
||||||
@ -53,5 +51,4 @@ contextBridge.exposeInMainWorld(
|
|||||||
}
|
}
|
||||||
ipcRenderer.send(channel, ...args);
|
ipcRenderer.send(channel, ...args);
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
@ -67,7 +67,7 @@ function writeStore(data: Record<string, string>): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function recordSSOSession(sessionID: string): void {
|
export function recordSSOSession(sessionID: string): void {
|
||||||
const userDataPath = app.getPath('userData');
|
const userDataPath = app.getPath("userData");
|
||||||
const store = readStore();
|
const store = readStore();
|
||||||
for (const key in store) {
|
for (const key in store) {
|
||||||
// ensure each instance only has one (the latest) session ID to prevent the file growing unbounded
|
// ensure each instance only has one (the latest) session ID to prevent the file growing unbounded
|
||||||
@ -82,7 +82,7 @@ export function recordSSOSession(sessionID: string): void {
|
|||||||
|
|
||||||
export function getProfileFromDeeplink(args: string[]): string | undefined {
|
export function getProfileFromDeeplink(args: string[]): string | undefined {
|
||||||
// check if we are passed a profile in the SSO callback url
|
// check if we are passed a profile in the SSO callback url
|
||||||
const deeplinkUrl = args.find(arg => arg.startsWith(PROTOCOL + '//'));
|
const deeplinkUrl = args.find((arg) => arg.startsWith(PROTOCOL + "//"));
|
||||||
if (deeplinkUrl?.includes(SEARCH_PARAM)) {
|
if (deeplinkUrl?.includes(SEARCH_PARAM)) {
|
||||||
const parsedUrl = new URL(deeplinkUrl);
|
const parsedUrl = new URL(deeplinkUrl);
|
||||||
if (parsedUrl.protocol === PROTOCOL) {
|
if (parsedUrl.protocol === PROTOCOL) {
|
||||||
@ -98,25 +98,26 @@ export function protocolInit(): void {
|
|||||||
// get all args except `hidden` as it'd mean the app would not get focused
|
// get all args except `hidden` as it'd mean the app would not get focused
|
||||||
// XXX: passing args to protocol handlers only works on Windows, so unpackaged deep-linking
|
// XXX: passing args to protocol handlers only works on Windows, so unpackaged deep-linking
|
||||||
// --profile/--profile-dir are passed via the SEARCH_PARAM var in the callback url
|
// --profile/--profile-dir are passed via the SEARCH_PARAM var in the callback url
|
||||||
const args = process.argv.slice(1).filter(arg => arg !== "--hidden" && arg !== "-hidden");
|
const args = process.argv.slice(1).filter((arg) => arg !== "--hidden" && arg !== "-hidden");
|
||||||
if (app.isPackaged) {
|
if (app.isPackaged) {
|
||||||
app.setAsDefaultProtocolClient('element', process.execPath, args);
|
app.setAsDefaultProtocolClient("element", process.execPath, args);
|
||||||
} else if (process.platform === 'win32') { // on Mac/Linux this would just cause the electron binary to open
|
} else if (process.platform === "win32") {
|
||||||
|
// on Mac/Linux this would just cause the electron binary to open
|
||||||
// special handler for running without being packaged, e.g `electron .` by passing our app path to electron
|
// special handler for running without being packaged, e.g `electron .` by passing our app path to electron
|
||||||
app.setAsDefaultProtocolClient('element', process.execPath, [app.getAppPath(), ...args]);
|
app.setAsDefaultProtocolClient("element", process.execPath, [app.getAppPath(), ...args]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === "darwin") {
|
||||||
// Protocol handler for macos
|
// Protocol handler for macos
|
||||||
app.on('open-url', function(ev, url) {
|
app.on("open-url", function (ev, url) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
processUrl(url);
|
processUrl(url);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Protocol handler for win32/Linux
|
// Protocol handler for win32/Linux
|
||||||
app.on('second-instance', (ev, commandLine) => {
|
app.on("second-instance", (ev, commandLine) => {
|
||||||
const url = commandLine[commandLine.length - 1];
|
const url = commandLine[commandLine.length - 1];
|
||||||
if (!url.startsWith(PROTOCOL + '//')) return;
|
if (!url.startsWith(PROTOCOL + "//")) return;
|
||||||
processUrl(url);
|
processUrl(url);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ let ReindexError: typeof ReindexErrorType;
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const seshatModule = require('matrix-seshat');
|
const seshatModule = require("matrix-seshat");
|
||||||
Seshat = seshatModule.Seshat;
|
Seshat = seshatModule.Seshat;
|
||||||
SeshatRecovery = seshatModule.SeshatRecovery;
|
SeshatRecovery = seshatModule.SeshatRecovery;
|
||||||
ReindexError = seshatModule.ReindexError;
|
ReindexError = seshatModule.ReindexError;
|
||||||
@ -75,29 +75,29 @@ const deleteContents = async (p: string): Promise<void> => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
ipcMain.on("seshat", async function (_ev: IpcMainEvent, payload): Promise<void> {
|
||||||
if (!global.mainWindow) return;
|
if (!global.mainWindow) return;
|
||||||
|
|
||||||
// We do this here to ensure we get the path after --profile has been resolved
|
// We do this here to ensure we get the path after --profile has been resolved
|
||||||
const eventStorePath = path.join(app.getPath('userData'), 'EventStore');
|
const eventStorePath = path.join(app.getPath("userData"), "EventStore");
|
||||||
|
|
||||||
const sendError = (id: string, e: Error): void => {
|
const sendError = (id: string, e: Error): void => {
|
||||||
const error = {
|
const error = {
|
||||||
message: e.message,
|
message: e.message,
|
||||||
};
|
};
|
||||||
|
|
||||||
global.mainWindow?.webContents.send('seshatReply', { id, error });
|
global.mainWindow?.webContents.send("seshatReply", { id, error });
|
||||||
};
|
};
|
||||||
|
|
||||||
const args = payload.args || [];
|
const args = payload.args || [];
|
||||||
let ret: any;
|
let ret: any;
|
||||||
|
|
||||||
switch (payload.name) {
|
switch (payload.name) {
|
||||||
case 'supportsEventIndexing':
|
case "supportsEventIndexing":
|
||||||
ret = seshatSupported;
|
ret = seshatSupported;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'initEventIndex':
|
case "initEventIndex":
|
||||||
if (eventIndex === null) {
|
if (eventIndex === null) {
|
||||||
const userId = args[0];
|
const userId = args[0];
|
||||||
const deviceId = args[1];
|
const deviceId = args[1];
|
||||||
@ -127,8 +127,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteContents(eventStorePath);
|
await deleteContents(eventStorePath);
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
await recoveryIndex.reindex();
|
await recoveryIndex.reindex();
|
||||||
}
|
}
|
||||||
@ -142,7 +141,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'closeEventIndex':
|
case "closeEventIndex":
|
||||||
if (eventIndex !== null) {
|
if (eventIndex !== null) {
|
||||||
const index = eventIndex;
|
const index = eventIndex;
|
||||||
eventIndex = null;
|
eventIndex = null;
|
||||||
@ -156,26 +155,24 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'deleteEventIndex': {
|
case "deleteEventIndex": {
|
||||||
try {
|
try {
|
||||||
await deleteContents(eventStorePath);
|
await deleteContents(eventStorePath);
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'isEventIndexEmpty':
|
case "isEventIndexEmpty":
|
||||||
if (eventIndex === null) ret = true;
|
if (eventIndex === null) ret = true;
|
||||||
else ret = await eventIndex.isEmpty();
|
else ret = await eventIndex.isEmpty();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'isRoomIndexed':
|
case "isRoomIndexed":
|
||||||
if (eventIndex === null) ret = false;
|
if (eventIndex === null) ret = false;
|
||||||
else ret = await eventIndex.isRoomIndexed(args[0]);
|
else ret = await eventIndex.isRoomIndexed(args[0]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'addEventToIndex':
|
case "addEventToIndex":
|
||||||
try {
|
try {
|
||||||
eventIndex?.addEvent(args[0], args[1]);
|
eventIndex?.addEvent(args[0], args[1]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -184,7 +181,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'deleteEvent':
|
case "deleteEvent":
|
||||||
try {
|
try {
|
||||||
ret = await eventIndex?.deleteEvent(args[0]);
|
ret = await eventIndex?.deleteEvent(args[0]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -193,7 +190,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'commitLiveEvents':
|
case "commitLiveEvents":
|
||||||
try {
|
try {
|
||||||
ret = await eventIndex?.commit();
|
ret = await eventIndex?.commit();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -202,7 +199,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'searchEventIndex':
|
case "searchEventIndex":
|
||||||
try {
|
try {
|
||||||
ret = await eventIndex?.search(args[0]);
|
ret = await eventIndex?.search(args[0]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -211,12 +208,11 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'addHistoricEvents':
|
case "addHistoricEvents":
|
||||||
if (eventIndex === null) ret = false;
|
if (eventIndex === null) ret = false;
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
ret = await eventIndex.addHistoricEvents(
|
ret = await eventIndex.addHistoricEvents(args[0], args[1], args[2]);
|
||||||
args[0], args[1], args[2]);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
sendError(payload.id, <Error>e);
|
sendError(payload.id, <Error>e);
|
||||||
return;
|
return;
|
||||||
@ -224,7 +220,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'getStats':
|
case "getStats":
|
||||||
if (eventIndex === null) ret = 0;
|
if (eventIndex === null) ret = 0;
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@ -236,7 +232,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'removeCrawlerCheckpoint':
|
case "removeCrawlerCheckpoint":
|
||||||
if (eventIndex === null) ret = false;
|
if (eventIndex === null) ret = false;
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@ -248,7 +244,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'addCrawlerCheckpoint':
|
case "addCrawlerCheckpoint":
|
||||||
if (eventIndex === null) ret = false;
|
if (eventIndex === null) ret = false;
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@ -260,7 +256,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'loadFileEvents':
|
case "loadFileEvents":
|
||||||
if (eventIndex === null) ret = [];
|
if (eventIndex === null) ret = [];
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@ -272,7 +268,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'loadCheckpoints':
|
case "loadCheckpoints":
|
||||||
if (eventIndex === null) ret = [];
|
if (eventIndex === null) ret = [];
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@ -283,7 +279,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'setUserVersion':
|
case "setUserVersion":
|
||||||
if (eventIndex === null) break;
|
if (eventIndex === null) break;
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@ -295,7 +291,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'getUserVersion':
|
case "getUserVersion":
|
||||||
if (eventIndex === null) ret = 0;
|
if (eventIndex === null) ret = 0;
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@ -308,14 +304,14 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
global.mainWindow.webContents.send('seshatReply', {
|
global.mainWindow.webContents.send("seshatReply", {
|
||||||
id: payload.id,
|
id: payload.id,
|
||||||
error: "Unknown IPC Call: " + payload.name,
|
error: "Unknown IPC Call: " + payload.name,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
global.mainWindow.webContents.send('seshatReply', {
|
global.mainWindow.webContents.send("seshatReply", {
|
||||||
id: payload.id,
|
id: payload.id,
|
||||||
reply: ret,
|
reply: ret,
|
||||||
});
|
});
|
||||||
|
@ -42,17 +42,19 @@ export const Settings: Record<string, Setting> = {
|
|||||||
global.store.set("warnBeforeExit", value);
|
global.store.set("warnBeforeExit", value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Electron.alwaysShowMenuBar": { // not supported on macOS
|
"Electron.alwaysShowMenuBar": {
|
||||||
|
// not supported on macOS
|
||||||
async read(): Promise<any> {
|
async read(): Promise<any> {
|
||||||
return !global.mainWindow!.autoHideMenuBar;
|
return !global.mainWindow!.autoHideMenuBar;
|
||||||
},
|
},
|
||||||
async write(value: any): Promise<void> {
|
async write(value: any): Promise<void> {
|
||||||
global.store.set('autoHideMenuBar', !value);
|
global.store.set("autoHideMenuBar", !value);
|
||||||
global.mainWindow!.autoHideMenuBar = !value;
|
global.mainWindow!.autoHideMenuBar = !value;
|
||||||
global.mainWindow!.setMenuBarVisibility(value);
|
global.mainWindow!.setMenuBarVisibility(value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Electron.showTrayIcon": { // not supported on macOS
|
"Electron.showTrayIcon": {
|
||||||
|
// not supported on macOS
|
||||||
async read(): Promise<any> {
|
async read(): Promise<any> {
|
||||||
return tray.hasTray();
|
return tray.hasTray();
|
||||||
},
|
},
|
||||||
@ -63,15 +65,15 @@ export const Settings: Record<string, Setting> = {
|
|||||||
} else {
|
} else {
|
||||||
tray.destroy();
|
tray.destroy();
|
||||||
}
|
}
|
||||||
global.store.set('minimizeToTray', value);
|
global.store.set("minimizeToTray", value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Electron.enableHardwareAcceleration": {
|
"Electron.enableHardwareAcceleration": {
|
||||||
async read(): Promise<any> {
|
async read(): Promise<any> {
|
||||||
return !global.store.get('disableHardwareAcceleration', false);
|
return !global.store.get("disableHardwareAcceleration", false);
|
||||||
},
|
},
|
||||||
async write(value: any): Promise<void> {
|
async write(value: any): Promise<void> {
|
||||||
global.store.set('disableHardwareAcceleration', !value);
|
global.store.set("disableHardwareAcceleration", !value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -23,29 +23,29 @@ function runUpdateExe(args: string[]): Promise<void> {
|
|||||||
// Note that there's an Update.exe in the app-x.x.x directory and one in the parent
|
// Note that there's an Update.exe in the app-x.x.x directory and one in the parent
|
||||||
// directory: we need to run the one in the parent directory, because it discovers
|
// directory: we need to run the one in the parent directory, because it discovers
|
||||||
// information about the app by inspecting the directory it's run from.
|
// information about the app by inspecting the directory it's run from.
|
||||||
const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe');
|
const updateExe = path.resolve(path.dirname(process.execPath), "..", "Update.exe");
|
||||||
console.log(`Spawning '${updateExe}' with args '${args}'`);
|
console.log(`Spawning '${updateExe}' with args '${args}'`);
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
spawn(updateExe, args, {
|
spawn(updateExe, args, {
|
||||||
detached: true,
|
detached: true,
|
||||||
}).on('close', resolve);
|
}).on("close", resolve);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkSquirrelHooks(): boolean {
|
function checkSquirrelHooks(): boolean {
|
||||||
if (process.platform !== 'win32') return false;
|
if (process.platform !== "win32") return false;
|
||||||
const cmd = process.argv[1];
|
const cmd = process.argv[1];
|
||||||
const target = path.basename(process.execPath);
|
const target = path.basename(process.execPath);
|
||||||
if (cmd === '--squirrel-install') {
|
if (cmd === "--squirrel-install") {
|
||||||
runUpdateExe(['--createShortcut=' + target]).then(() => app.quit());
|
runUpdateExe(["--createShortcut=" + target]).then(() => app.quit());
|
||||||
return true;
|
return true;
|
||||||
} else if (cmd === '--squirrel-updated') {
|
} else if (cmd === "--squirrel-updated") {
|
||||||
app.quit();
|
app.quit();
|
||||||
return true;
|
return true;
|
||||||
} else if (cmd === '--squirrel-uninstall') {
|
} else if (cmd === "--squirrel-uninstall") {
|
||||||
runUpdateExe(['--removeShortcut=' + target]).then(() => app.quit());
|
runUpdateExe(["--removeShortcut=" + target]).then(() => app.quit());
|
||||||
return true;
|
return true;
|
||||||
} else if (cmd === '--squirrel-obsolete') {
|
} else if (cmd === "--squirrel-obsolete") {
|
||||||
app.quit();
|
app.quit();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
24
src/tray.ts
24
src/tray.ts
@ -25,7 +25,7 @@ import { _t } from "./language-helper";
|
|||||||
let trayIcon: Tray | null = null;
|
let trayIcon: Tray | null = null;
|
||||||
|
|
||||||
export function hasTray(): boolean {
|
export function hasTray(): boolean {
|
||||||
return (trayIcon !== null);
|
return trayIcon !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function destroy(): void {
|
export function destroy(): void {
|
||||||
@ -52,17 +52,17 @@ interface IConfig {
|
|||||||
|
|
||||||
export function create(config: IConfig): void {
|
export function create(config: IConfig): void {
|
||||||
// no trays on darwin
|
// no trays on darwin
|
||||||
if (process.platform === 'darwin' || trayIcon) return;
|
if (process.platform === "darwin" || trayIcon) return;
|
||||||
const defaultIcon = nativeImage.createFromPath(config.icon_path);
|
const defaultIcon = nativeImage.createFromPath(config.icon_path);
|
||||||
|
|
||||||
trayIcon = new Tray(defaultIcon);
|
trayIcon = new Tray(defaultIcon);
|
||||||
trayIcon.setToolTip(config.brand);
|
trayIcon.setToolTip(config.brand);
|
||||||
initApplicationMenu();
|
initApplicationMenu();
|
||||||
trayIcon.on('click', toggleWin);
|
trayIcon.on("click", toggleWin);
|
||||||
|
|
||||||
let lastFavicon: string | null = null;
|
let lastFavicon: string | null = null;
|
||||||
global.mainWindow?.webContents.on('page-favicon-updated', async function(ev, favicons) {
|
global.mainWindow?.webContents.on("page-favicon-updated", async function (ev, favicons) {
|
||||||
if (!favicons || favicons.length <= 0 || !favicons[0].startsWith('data:')) {
|
if (!favicons || favicons.length <= 0 || !favicons[0].startsWith("data:")) {
|
||||||
if (lastFavicon !== null) {
|
if (lastFavicon !== null) {
|
||||||
global.mainWindow?.setIcon(defaultIcon);
|
global.mainWindow?.setIcon(defaultIcon);
|
||||||
trayIcon?.setImage(defaultIcon);
|
trayIcon?.setImage(defaultIcon);
|
||||||
@ -78,9 +78,9 @@ export function create(config: IConfig): void {
|
|||||||
let newFavicon = nativeImage.createFromDataURL(favicons[0]);
|
let newFavicon = nativeImage.createFromDataURL(favicons[0]);
|
||||||
|
|
||||||
// Windows likes ico's too much.
|
// Windows likes ico's too much.
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === "win32") {
|
||||||
try {
|
try {
|
||||||
const icoPath = path.join(app.getPath('temp'), 'win32_element_icon.ico');
|
const icoPath = path.join(app.getPath("temp"), "win32_element_icon.ico");
|
||||||
fs.writeFileSync(icoPath, await pngToIco(newFavicon.toPNG()));
|
fs.writeFileSync(icoPath, await pngToIco(newFavicon.toPNG()));
|
||||||
newFavicon = nativeImage.createFromPath(icoPath);
|
newFavicon = nativeImage.createFromPath(icoPath);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -92,7 +92,7 @@ export function create(config: IConfig): void {
|
|||||||
global.mainWindow?.setIcon(newFavicon);
|
global.mainWindow?.setIcon(newFavicon);
|
||||||
});
|
});
|
||||||
|
|
||||||
global.mainWindow?.webContents.on('page-title-updated', function(ev, title) {
|
global.mainWindow?.webContents.on("page-title-updated", function (ev, title) {
|
||||||
trayIcon?.setToolTip(title);
|
trayIcon?.setToolTip(title);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -104,13 +104,13 @@ export function initApplicationMenu(): void {
|
|||||||
|
|
||||||
const contextMenu = Menu.buildFromTemplate([
|
const contextMenu = Menu.buildFromTemplate([
|
||||||
{
|
{
|
||||||
label: _t('Show/Hide'),
|
label: _t("Show/Hide"),
|
||||||
click: toggleWin,
|
click: toggleWin,
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: _t('Quit'),
|
label: _t("Quit"),
|
||||||
click: function(): void {
|
click: function (): void {
|
||||||
app.quit();
|
app.quit();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -37,33 +37,33 @@ function pollForUpdates(): void {
|
|||||||
autoUpdater.checkForUpdates();
|
autoUpdater.checkForUpdates();
|
||||||
} else {
|
} else {
|
||||||
console.log("Skipping update check as download already present");
|
console.log("Skipping update check as download already present");
|
||||||
global.mainWindow?.webContents.send('update-downloaded', latestUpdateDownloaded);
|
global.mainWindow?.webContents.send("update-downloaded", latestUpdateDownloaded);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Couldn\'t check for update', e);
|
console.log("Couldn't check for update", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function start(updateBaseUrl: string): void {
|
export function start(updateBaseUrl: string): void {
|
||||||
if (updateBaseUrl.slice(-1) !== '/') {
|
if (updateBaseUrl.slice(-1) !== "/") {
|
||||||
updateBaseUrl = updateBaseUrl + '/';
|
updateBaseUrl = updateBaseUrl + "/";
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
let url: string;
|
let url: string;
|
||||||
let serverType: "json" | undefined;
|
let serverType: "json" | undefined;
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === "darwin") {
|
||||||
// On macOS it takes a JSON file with a map between versions and their URLs
|
// On macOS it takes a JSON file with a map between versions and their URLs
|
||||||
url = `${updateBaseUrl}macos/releases.json`;
|
url = `${updateBaseUrl}macos/releases.json`;
|
||||||
serverType = "json";
|
serverType = "json";
|
||||||
} else if (process.platform === 'win32') {
|
} else if (process.platform === "win32") {
|
||||||
// On windows it takes a base path and looks for files under that path.
|
// On windows it takes a base path and looks for files under that path.
|
||||||
url = `${updateBaseUrl}win32/${process.arch}/`;
|
url = `${updateBaseUrl}win32/${process.arch}/`;
|
||||||
} else {
|
} else {
|
||||||
// Squirrel / electron only supports auto-update on these two platforms.
|
// Squirrel / electron only supports auto-update on these two platforms.
|
||||||
// I'm not even going to try to guess which feed style they'd use if they
|
// I'm not even going to try to guess which feed style they'd use if they
|
||||||
// implemented it on Linux, or if it would be different again.
|
// implemented it on Linux, or if it would be different again.
|
||||||
console.log('Auto update not supported on this platform');
|
console.log("Auto update not supported on this platform");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,15 +82,15 @@ export function start(updateBaseUrl: string): void {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// will fail if running in debug mode
|
// will fail if running in debug mode
|
||||||
console.log('Couldn\'t enable update checking', err);
|
console.log("Couldn't enable update checking", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.on('install_update', installUpdate);
|
ipcMain.on("install_update", installUpdate);
|
||||||
ipcMain.on('check_updates', pollForUpdates);
|
ipcMain.on("check_updates", pollForUpdates);
|
||||||
|
|
||||||
function ipcChannelSendUpdateStatus(status: boolean | string): void {
|
function ipcChannelSendUpdateStatus(status: boolean | string): void {
|
||||||
global.mainWindow?.webContents.send('check_updates', status);
|
global.mainWindow?.webContents.send("check_updates", status);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ICachedUpdate {
|
interface ICachedUpdate {
|
||||||
@ -102,23 +102,26 @@ interface ICachedUpdate {
|
|||||||
|
|
||||||
// cache the latest update which has been downloaded as electron offers no api to read it
|
// cache the latest update which has been downloaded as electron offers no api to read it
|
||||||
let latestUpdateDownloaded: ICachedUpdate;
|
let latestUpdateDownloaded: ICachedUpdate;
|
||||||
autoUpdater.on('update-available', function() {
|
autoUpdater
|
||||||
|
.on("update-available", function () {
|
||||||
ipcChannelSendUpdateStatus(true);
|
ipcChannelSendUpdateStatus(true);
|
||||||
}).on('update-not-available', function() {
|
})
|
||||||
|
.on("update-not-available", function () {
|
||||||
if (latestUpdateDownloaded) {
|
if (latestUpdateDownloaded) {
|
||||||
// the only time we will get `update-not-available` if `latestUpdateDownloaded` is already set
|
// the only time we will get `update-not-available` if `latestUpdateDownloaded` is already set
|
||||||
// is if the user used the Manual Update check and there is no update newer than the one we
|
// is if the user used the Manual Update check and there is no update newer than the one we
|
||||||
// have downloaded, so show it to them as the latest again.
|
// have downloaded, so show it to them as the latest again.
|
||||||
global.mainWindow?.webContents.send('update-downloaded', latestUpdateDownloaded);
|
global.mainWindow?.webContents.send("update-downloaded", latestUpdateDownloaded);
|
||||||
} else {
|
} else {
|
||||||
ipcChannelSendUpdateStatus(false);
|
ipcChannelSendUpdateStatus(false);
|
||||||
}
|
}
|
||||||
}).on('error', function(error) {
|
})
|
||||||
|
.on("error", function (error) {
|
||||||
ipcChannelSendUpdateStatus(error.message);
|
ipcChannelSendUpdateStatus(error.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
autoUpdater.on('update-downloaded', (ev, releaseNotes, releaseName, releaseDate, updateURL) => {
|
autoUpdater.on("update-downloaded", (ev, releaseNotes, releaseName, releaseDate, updateURL) => {
|
||||||
// forward to renderer
|
// forward to renderer
|
||||||
latestUpdateDownloaded = { releaseNotes, releaseName, releaseDate, updateURL };
|
latestUpdateDownloaded = { releaseNotes, releaseName, releaseDate, updateURL };
|
||||||
global.mainWindow?.webContents.send('update-downloaded', latestUpdateDownloaded);
|
global.mainWindow?.webContents.send("update-downloaded", latestUpdateDownloaded);
|
||||||
});
|
});
|
||||||
|
@ -22,7 +22,7 @@ export async function randomArray(size: number): Promise<string> {
|
|||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
resolve(buf.toString("base64").replace(/=+$/g, ''));
|
resolve(buf.toString("base64").replace(/=+$/g, ""));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -14,125 +14,133 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { app, shell, Menu, MenuItem, MenuItemConstructorOptions } from 'electron';
|
import { app, shell, Menu, MenuItem, MenuItemConstructorOptions } from "electron";
|
||||||
|
|
||||||
import { _t } from './language-helper';
|
import { _t } from "./language-helper";
|
||||||
|
|
||||||
const isMac = process.platform === 'darwin';
|
const isMac = process.platform === "darwin";
|
||||||
|
|
||||||
export function buildMenuTemplate(): Menu {
|
export function buildMenuTemplate(): Menu {
|
||||||
// Menu template from http://electron.atom.io/docs/api/menu/, edited
|
// Menu template from http://electron.atom.io/docs/api/menu/, edited
|
||||||
const template: Array<(MenuItemConstructorOptions) | (MenuItem)> = [
|
const template: Array<MenuItemConstructorOptions | MenuItem> = [
|
||||||
{
|
{
|
||||||
label: _t('Edit'),
|
label: _t("Edit"),
|
||||||
accelerator: 'e',
|
accelerator: "e",
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
role: 'undo',
|
role: "undo",
|
||||||
label: _t('Undo'),
|
label: _t("Undo"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'redo',
|
role: "redo",
|
||||||
label: _t('Redo'),
|
label: _t("Redo"),
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
role: 'cut',
|
role: "cut",
|
||||||
label: _t('Cut'),
|
label: _t("Cut"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'copy',
|
role: "copy",
|
||||||
label: _t('Copy'),
|
label: _t("Copy"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'paste',
|
role: "paste",
|
||||||
label: _t('Paste'),
|
label: _t("Paste"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'pasteAndMatchStyle',
|
role: "pasteAndMatchStyle",
|
||||||
label: _t('Paste and Match Style'),
|
label: _t("Paste and Match Style"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'delete',
|
role: "delete",
|
||||||
label: _t('Delete'),
|
label: _t("Delete"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'selectAll',
|
role: "selectAll",
|
||||||
label: _t('Select All'),
|
label: _t("Select All"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _t('View'),
|
label: _t("View"),
|
||||||
accelerator: 'V',
|
accelerator: "V",
|
||||||
submenu: [
|
submenu: [
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
role: 'resetZoom',
|
role: "resetZoom",
|
||||||
accelerator: 'CmdOrCtrl+Num0',
|
accelerator: "CmdOrCtrl+Num0",
|
||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'zoomIn',
|
role: "zoomIn",
|
||||||
accelerator: 'CmdOrCtrl+NumAdd',
|
accelerator: "CmdOrCtrl+NumAdd",
|
||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'zoomOut',
|
role: "zoomOut",
|
||||||
accelerator: 'CmdOrCtrl+NumSub',
|
accelerator: "CmdOrCtrl+NumSub",
|
||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'resetZoom',
|
role: "resetZoom",
|
||||||
label: _t('Actual Size'),
|
label: _t("Actual Size"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'zoomIn',
|
role: "zoomIn",
|
||||||
label: _t('Zoom In'),
|
label: _t("Zoom In"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'zoomOut',
|
role: "zoomOut",
|
||||||
label: _t('Zoom Out'),
|
label: _t("Zoom Out"),
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
// in macOS the Preferences menu item goes in the first menu
|
// in macOS the Preferences menu item goes in the first menu
|
||||||
...(!isMac ? [{
|
...(!isMac
|
||||||
label: _t('Preferences'),
|
? [
|
||||||
click(): void { global.mainWindow?.webContents.send('preferences'); },
|
|
||||||
}] : []),
|
|
||||||
{
|
{
|
||||||
role: 'togglefullscreen',
|
label: _t("Preferences"),
|
||||||
label: _t('Toggle Full Screen'),
|
click(): void {
|
||||||
|
global.mainWindow?.webContents.send("preferences");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
{
|
||||||
|
role: "togglefullscreen",
|
||||||
|
label: _t("Toggle Full Screen"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'toggleDevTools',
|
role: "toggleDevTools",
|
||||||
label: _t('Toggle Developer Tools'),
|
label: _t("Toggle Developer Tools"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _t('Window'),
|
label: _t("Window"),
|
||||||
accelerator: 'w',
|
accelerator: "w",
|
||||||
role: 'window',
|
role: "window",
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
role: 'minimize',
|
role: "minimize",
|
||||||
label: _t('Minimize'),
|
label: _t("Minimize"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'close',
|
role: "close",
|
||||||
label: _t('Close'),
|
label: _t("Close"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _t('Help'),
|
label: _t("Help"),
|
||||||
accelerator: 'h',
|
accelerator: "h",
|
||||||
role: 'help',
|
role: "help",
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: _t('Element Help'),
|
label: _t("Element Help"),
|
||||||
click(): void { shell.openExternal('https://element.io/help'); },
|
click(): void {
|
||||||
|
shell.openExternal("https://element.io/help");
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -142,92 +150,95 @@ export function buildMenuTemplate(): Menu {
|
|||||||
if (isMac) {
|
if (isMac) {
|
||||||
template.unshift({
|
template.unshift({
|
||||||
// first macOS menu is the name of the app
|
// first macOS menu is the name of the app
|
||||||
role: 'appMenu',
|
role: "appMenu",
|
||||||
label: app.name,
|
label: app.name,
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
role: 'about',
|
role: "about",
|
||||||
label: _t('About') + ' ' + app.name,
|
label: _t("About") + " " + app.name,
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: _t('Preferences') + '…',
|
label: _t("Preferences") + "…",
|
||||||
accelerator: 'Command+,', // Mac-only accelerator
|
accelerator: "Command+,", // Mac-only accelerator
|
||||||
click(): void { global.mainWindow?.webContents.send('preferences'); },
|
click(): void {
|
||||||
|
global.mainWindow?.webContents.send("preferences");
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
},
|
||||||
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
role: 'services',
|
role: "services",
|
||||||
label: _t('Services'),
|
label: _t("Services"),
|
||||||
submenu: [],
|
submenu: [],
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
role: 'hide',
|
role: "hide",
|
||||||
label: _t('Hide'),
|
label: _t("Hide"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'hideOthers',
|
role: "hideOthers",
|
||||||
label: _t('Hide Others'),
|
label: _t("Hide Others"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'unhide',
|
role: "unhide",
|
||||||
label: _t('Unhide'),
|
label: _t("Unhide"),
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
role: 'quit',
|
role: "quit",
|
||||||
label: _t('Quit'),
|
label: _t("Quit"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
// Edit menu.
|
// Edit menu.
|
||||||
// This has a 'speech' section on macOS
|
// This has a 'speech' section on macOS
|
||||||
(template[1].submenu as MenuItemConstructorOptions[]).push(
|
(template[1].submenu as MenuItemConstructorOptions[]).push(
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: _t('Speech'),
|
label: _t("Speech"),
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
role: 'startSpeaking',
|
role: "startSpeaking",
|
||||||
label: _t('Start Speaking'),
|
label: _t("Start Speaking"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'stopSpeaking',
|
role: "stopSpeaking",
|
||||||
label: _t('Stop Speaking'),
|
label: _t("Stop Speaking"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Window menu.
|
// Window menu.
|
||||||
// This also has specific functionality on macOS
|
// This also has specific functionality on macOS
|
||||||
template[3].submenu = [
|
template[3].submenu = [
|
||||||
{
|
{
|
||||||
label: _t('Close'),
|
label: _t("Close"),
|
||||||
accelerator: 'CmdOrCtrl+W',
|
accelerator: "CmdOrCtrl+W",
|
||||||
role: 'close',
|
role: "close",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _t('Minimize'),
|
label: _t("Minimize"),
|
||||||
accelerator: 'CmdOrCtrl+M',
|
accelerator: "CmdOrCtrl+M",
|
||||||
role: 'minimize',
|
role: "minimize",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _t('Zoom'),
|
label: _t("Zoom"),
|
||||||
role: 'zoom',
|
role: "zoom",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'separator',
|
type: "separator",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _t('Bring All to Front'),
|
label: _t("Bring All to Front"),
|
||||||
role: 'front',
|
role: "front",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
template.unshift({
|
template.unshift({
|
||||||
label: _t('File'),
|
label: _t("File"),
|
||||||
accelerator: 'f',
|
accelerator: "f",
|
||||||
submenu: [
|
submenu: [
|
||||||
// For some reason, 'about' does not seem to work on windows.
|
// For some reason, 'about' does not seem to work on windows.
|
||||||
/*{
|
/*{
|
||||||
@ -235,8 +246,8 @@ export function buildMenuTemplate(): Menu {
|
|||||||
label: _t('About'),
|
label: _t('About'),
|
||||||
},*/
|
},*/
|
||||||
{
|
{
|
||||||
role: 'quit',
|
role: "quit",
|
||||||
label: _t('Quit'),
|
label: _t("Quit"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@ -28,22 +28,18 @@ import {
|
|||||||
DownloadItem,
|
DownloadItem,
|
||||||
MenuItemConstructorOptions,
|
MenuItemConstructorOptions,
|
||||||
IpcMainEvent,
|
IpcMainEvent,
|
||||||
} from 'electron';
|
} from "electron";
|
||||||
import url from 'url';
|
import url from "url";
|
||||||
import fs from 'fs';
|
import fs from "fs";
|
||||||
import fetch from 'node-fetch';
|
import fetch from "node-fetch";
|
||||||
import { pipeline } from 'stream';
|
import { pipeline } from "stream";
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
|
|
||||||
import { _t } from './language-helper';
|
import { _t } from "./language-helper";
|
||||||
|
|
||||||
const MAILTO_PREFIX = "mailto:";
|
const MAILTO_PREFIX = "mailto:";
|
||||||
|
|
||||||
const PERMITTED_URL_SCHEMES: string[] = [
|
const PERMITTED_URL_SCHEMES: string[] = ["http:", "https:", MAILTO_PREFIX];
|
||||||
'http:',
|
|
||||||
'https:',
|
|
||||||
MAILTO_PREFIX,
|
|
||||||
];
|
|
||||||
|
|
||||||
function safeOpenURL(target: string): void {
|
function safeOpenURL(target: string): void {
|
||||||
// openExternal passes the target to open/start/xdg-open,
|
// openExternal passes the target to open/start/xdg-open,
|
||||||
@ -70,7 +66,7 @@ function onWindowOrNavigate(ev: Event, target: string): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function writeNativeImage(filePath: string, img: NativeImage): Promise<void> {
|
function writeNativeImage(filePath: string, img: NativeImage): Promise<void> {
|
||||||
switch (filePath.split('.').pop()?.toLowerCase()) {
|
switch (filePath.split(".").pop()?.toLowerCase()) {
|
||||||
case "jpg":
|
case "jpg":
|
||||||
case "jpeg":
|
case "jpeg":
|
||||||
return fs.promises.writeFile(filePath, img.toJPEG(100));
|
return fs.promises.writeFile(filePath, img.toJPEG(100));
|
||||||
@ -85,7 +81,7 @@ function writeNativeImage(filePath: string, img: NativeImage): Promise<void> {
|
|||||||
function onLinkContextMenu(ev: Event, params: ContextMenuParams, webContents: WebContents): void {
|
function onLinkContextMenu(ev: Event, params: ContextMenuParams, webContents: WebContents): void {
|
||||||
let url = params.linkURL || params.srcURL;
|
let url = params.linkURL || params.srcURL;
|
||||||
|
|
||||||
if (url.startsWith('vector://vector/webapp')) {
|
if (url.startsWith("vector://vector/webapp")) {
|
||||||
// Avoid showing a context menu for app icons
|
// Avoid showing a context menu for app icons
|
||||||
if (params.hasImageContents) return;
|
if (params.hasImageContents) return;
|
||||||
// Rewrite URL so that it can be used outside of the app
|
// Rewrite URL so that it can be used outside of the app
|
||||||
@ -94,55 +90,62 @@ function onLinkContextMenu(ev: Event, params: ContextMenuParams, webContents: We
|
|||||||
|
|
||||||
const popupMenu = new Menu();
|
const popupMenu = new Menu();
|
||||||
// No point trying to open blob: URLs in an external browser: it ain't gonna work.
|
// No point trying to open blob: URLs in an external browser: it ain't gonna work.
|
||||||
if (!url.startsWith('blob:')) {
|
if (!url.startsWith("blob:")) {
|
||||||
popupMenu.append(new MenuItem({
|
popupMenu.append(
|
||||||
|
new MenuItem({
|
||||||
label: url,
|
label: url,
|
||||||
click(): void {
|
click(): void {
|
||||||
safeOpenURL(url);
|
safeOpenURL(url);
|
||||||
},
|
},
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.hasImageContents) {
|
if (params.hasImageContents) {
|
||||||
popupMenu.append(new MenuItem({
|
popupMenu.append(
|
||||||
label: _t('Copy image'),
|
new MenuItem({
|
||||||
accelerator: 'c',
|
label: _t("Copy image"),
|
||||||
|
accelerator: "c",
|
||||||
click(): void {
|
click(): void {
|
||||||
webContents.copyImageAt(params.x, params.y);
|
webContents.copyImageAt(params.x, params.y);
|
||||||
},
|
},
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No point offering to copy a blob: URL either
|
// No point offering to copy a blob: URL either
|
||||||
if (!url.startsWith('blob:')) {
|
if (!url.startsWith("blob:")) {
|
||||||
// Special-case e-mail URLs to strip the `mailto:` like modern browsers do
|
// Special-case e-mail URLs to strip the `mailto:` like modern browsers do
|
||||||
if (url.startsWith(MAILTO_PREFIX)) {
|
if (url.startsWith(MAILTO_PREFIX)) {
|
||||||
popupMenu.append(new MenuItem({
|
popupMenu.append(
|
||||||
label: _t('Copy email address'),
|
new MenuItem({
|
||||||
accelerator: 'a',
|
label: _t("Copy email address"),
|
||||||
|
accelerator: "a",
|
||||||
click(): void {
|
click(): void {
|
||||||
clipboard.writeText(url.substr(MAILTO_PREFIX.length));
|
clipboard.writeText(url.substr(MAILTO_PREFIX.length));
|
||||||
},
|
},
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
popupMenu.append(new MenuItem({
|
popupMenu.append(
|
||||||
label: params.hasImageContents
|
new MenuItem({
|
||||||
? _t('Copy image address')
|
label: params.hasImageContents ? _t("Copy image address") : _t("Copy link address"),
|
||||||
: _t('Copy link address'),
|
accelerator: "a",
|
||||||
accelerator: 'a',
|
|
||||||
click(): void {
|
click(): void {
|
||||||
clipboard.writeText(url);
|
clipboard.writeText(url);
|
||||||
},
|
},
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: We cannot easily save a blob from the main process as
|
// XXX: We cannot easily save a blob from the main process as
|
||||||
// only the renderer can resolve them so don't give the user an option to.
|
// only the renderer can resolve them so don't give the user an option to.
|
||||||
if (params.hasImageContents && !url.startsWith('blob:')) {
|
if (params.hasImageContents && !url.startsWith("blob:")) {
|
||||||
popupMenu.append(new MenuItem({
|
popupMenu.append(
|
||||||
label: _t('Save image as...'),
|
new MenuItem({
|
||||||
accelerator: 's',
|
label: _t("Save image as..."),
|
||||||
|
accelerator: "s",
|
||||||
async click(): Promise<void> {
|
async click(): Promise<void> {
|
||||||
const targetFileName = params.suggestedFilename || params.altText || "image.png";
|
const targetFileName = params.suggestedFilename || params.altText || "image.png";
|
||||||
const { filePath } = await dialog.showSaveDialog({
|
const { filePath } = await dialog.showSaveDialog({
|
||||||
@ -169,7 +172,8 @@ function onLinkContextMenu(ev: Event, params: ContextMenuParams, webContents: We
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// popup() requires an options object even for no options
|
// popup() requires an options object even for no options
|
||||||
@ -181,7 +185,7 @@ function cutCopyPasteSelectContextMenus(params: ContextMenuParams): MenuItemCons
|
|||||||
const options: MenuItemConstructorOptions[] = [];
|
const options: MenuItemConstructorOptions[] = [];
|
||||||
|
|
||||||
if (params.misspelledWord) {
|
if (params.misspelledWord) {
|
||||||
params.dictionarySuggestions.forEach(word => {
|
params.dictionarySuggestions.forEach((word) => {
|
||||||
options.push({
|
options.push({
|
||||||
label: word,
|
label: word,
|
||||||
click: (menuItem, browserWindow) => {
|
click: (menuItem, browserWindow) => {
|
||||||
@ -189,42 +193,52 @@ function cutCopyPasteSelectContextMenus(params: ContextMenuParams): MenuItemCons
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
options.push({
|
options.push(
|
||||||
type: 'separator',
|
{
|
||||||
}, {
|
type: "separator",
|
||||||
label: _t('Add to dictionary'),
|
},
|
||||||
|
{
|
||||||
|
label: _t("Add to dictionary"),
|
||||||
click: (menuItem, browserWindow) => {
|
click: (menuItem, browserWindow) => {
|
||||||
browserWindow?.webContents.session.addWordToSpellCheckerDictionary(params.misspelledWord);
|
browserWindow?.webContents.session.addWordToSpellCheckerDictionary(params.misspelledWord);
|
||||||
},
|
},
|
||||||
}, {
|
},
|
||||||
type: 'separator',
|
{
|
||||||
});
|
type: "separator",
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
options.push({
|
options.push(
|
||||||
role: 'cut',
|
{
|
||||||
label: _t('Cut'),
|
role: "cut",
|
||||||
accelerator: 't',
|
label: _t("Cut"),
|
||||||
|
accelerator: "t",
|
||||||
enabled: params.editFlags.canCut,
|
enabled: params.editFlags.canCut,
|
||||||
}, {
|
},
|
||||||
role: 'copy',
|
{
|
||||||
label: _t('Copy'),
|
role: "copy",
|
||||||
accelerator: 'c',
|
label: _t("Copy"),
|
||||||
|
accelerator: "c",
|
||||||
enabled: params.editFlags.canCopy,
|
enabled: params.editFlags.canCopy,
|
||||||
}, {
|
},
|
||||||
role: 'paste',
|
{
|
||||||
label: _t('Paste'),
|
role: "paste",
|
||||||
accelerator: 'p',
|
label: _t("Paste"),
|
||||||
|
accelerator: "p",
|
||||||
enabled: params.editFlags.canPaste,
|
enabled: params.editFlags.canPaste,
|
||||||
}, {
|
},
|
||||||
role: 'pasteAndMatchStyle',
|
{
|
||||||
|
role: "pasteAndMatchStyle",
|
||||||
enabled: params.editFlags.canPaste,
|
enabled: params.editFlags.canPaste,
|
||||||
}, {
|
},
|
||||||
role: 'selectAll',
|
{
|
||||||
|
role: "selectAll",
|
||||||
label: _t("Select All"),
|
label: _t("Select All"),
|
||||||
accelerator: 'a',
|
accelerator: "a",
|
||||||
enabled: params.editFlags.canSelectAll,
|
enabled: params.editFlags.canSelectAll,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,9 +253,9 @@ function onSelectedContextMenu(ev: Event, params: ContextMenuParams): void {
|
|||||||
|
|
||||||
function onEditableContextMenu(ev: Event, params: ContextMenuParams): void {
|
function onEditableContextMenu(ev: Event, params: ContextMenuParams): void {
|
||||||
const items: MenuItemConstructorOptions[] = [
|
const items: MenuItemConstructorOptions[] = [
|
||||||
{ role: 'undo' },
|
{ role: "undo" },
|
||||||
{ role: 'redo', enabled: params.editFlags.canRedo },
|
{ role: "redo", enabled: params.editFlags.canRedo },
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
...cutCopyPasteSelectContextMenus(params),
|
...cutCopyPasteSelectContextMenus(params),
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -254,7 +268,7 @@ function onEditableContextMenu(ev: Event, params: ContextMenuParams): void {
|
|||||||
|
|
||||||
let userDownloadIndex = 0;
|
let userDownloadIndex = 0;
|
||||||
const userDownloadMap = new Map<number, string>(); // Map from id to path
|
const userDownloadMap = new Map<number, string>(); // Map from id to path
|
||||||
ipcMain.on('userDownloadAction', function(ev: IpcMainEvent, { id, open = false }) {
|
ipcMain.on("userDownloadAction", function (ev: IpcMainEvent, { id, open = false }) {
|
||||||
const path = userDownloadMap.get(id);
|
const path = userDownloadMap.get(id);
|
||||||
if (open && path) {
|
if (open && path) {
|
||||||
shell.openPath(path);
|
shell.openPath(path);
|
||||||
@ -268,12 +282,12 @@ export default (webContents: WebContents): void => {
|
|||||||
return { action: "deny" };
|
return { action: "deny" };
|
||||||
});
|
});
|
||||||
|
|
||||||
webContents.on('will-navigate', (ev: Event, target: string): void => {
|
webContents.on("will-navigate", (ev: Event, target: string): void => {
|
||||||
if (target.startsWith("vector://")) return;
|
if (target.startsWith("vector://")) return;
|
||||||
return onWindowOrNavigate(ev, target);
|
return onWindowOrNavigate(ev, target);
|
||||||
});
|
});
|
||||||
|
|
||||||
webContents.on('context-menu', function(ev: Event, params: ContextMenuParams): void {
|
webContents.on("context-menu", function (ev: Event, params: ContextMenuParams): void {
|
||||||
if (params.linkURL || params.srcURL) {
|
if (params.linkURL || params.srcURL) {
|
||||||
onLinkContextMenu(ev, params, webContents);
|
onLinkContextMenu(ev, params, webContents);
|
||||||
} else if (params.selectionText) {
|
} else if (params.selectionText) {
|
||||||
@ -283,13 +297,13 @@ export default (webContents: WebContents): void => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
webContents.session.on('will-download', (event: Event, item: DownloadItem): void => {
|
webContents.session.on("will-download", (event: Event, item: DownloadItem): void => {
|
||||||
item.once('done', (event, state) => {
|
item.once("done", (event, state) => {
|
||||||
if (state === 'completed') {
|
if (state === "completed") {
|
||||||
const savePath = item.getSavePath();
|
const savePath = item.getSavePath();
|
||||||
const id = userDownloadIndex++;
|
const id = userDownloadIndex++;
|
||||||
userDownloadMap.set(id, savePath);
|
userDownloadMap.set(id, savePath);
|
||||||
webContents.send('userDownloadCompleted', {
|
webContents.send("userDownloadCompleted", {
|
||||||
id,
|
id,
|
||||||
name: path.basename(savePath),
|
name: path.basename(savePath),
|
||||||
});
|
});
|
||||||
|
@ -45,13 +45,13 @@ describe("App launch", () => {
|
|||||||
args,
|
args,
|
||||||
recordVideo: {
|
recordVideo: {
|
||||||
dir: artifactsPath,
|
dir: artifactsPath,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
window = await app.firstWindow();
|
window = await app.firstWindow();
|
||||||
}, 30000);
|
}, 30000);
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
await app?.close().catch(e => {
|
await app?.close().catch((e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
});
|
});
|
||||||
fs.rmSync(tmpDir, { recursive: true });
|
fs.rmSync(tmpDir, { recursive: true });
|
||||||
|
@ -10,15 +10,9 @@
|
|||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"typeRoots": ["src/@types"],
|
"typeRoots": ["src/@types"],
|
||||||
"lib": [
|
"lib": ["es2019", "dom"],
|
||||||
"es2019",
|
|
||||||
"dom"
|
|
||||||
],
|
|
||||||
"types": ["jest", "node"],
|
"types": ["jest", "node"],
|
||||||
"strict": true
|
"strict": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["./src/**/*.ts", "./tests/**/*.ts"]
|
||||||
"./src/**/*.ts",
|
|
||||||
"./tests/**/*.ts"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
13
yarn.lock
13
yarn.lock
@ -4493,6 +4493,11 @@ eslint-config-google@^0.14.0:
|
|||||||
resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.14.0.tgz#4f5f8759ba6e11b424294a219dbfa18c508bcc1a"
|
resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.14.0.tgz#4f5f8759ba6e11b424294a219dbfa18c508bcc1a"
|
||||||
integrity sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==
|
integrity sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==
|
||||||
|
|
||||||
|
eslint-config-prettier@^8.5.0:
|
||||||
|
version "8.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1"
|
||||||
|
integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==
|
||||||
|
|
||||||
eslint-import-resolver-node@^0.3.6:
|
eslint-import-resolver-node@^0.3.6:
|
||||||
version "0.3.6"
|
version "0.3.6"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd"
|
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd"
|
||||||
@ -4527,10 +4532,10 @@ eslint-plugin-import@^2.25.4:
|
|||||||
resolve "^1.22.0"
|
resolve "^1.22.0"
|
||||||
tsconfig-paths "^3.14.1"
|
tsconfig-paths "^3.14.1"
|
||||||
|
|
||||||
eslint-plugin-matrix-org@^0.8.0:
|
eslint-plugin-matrix-org@^0.9.0:
|
||||||
version "0.8.0"
|
version "0.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-0.8.0.tgz#daa1396900a8cb1c1d88f1a370e45fc32482cd9e"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-0.9.0.tgz#b2a5186052ddbfa7dc9878779bafa5d68681c7b4"
|
||||||
integrity sha512-/Poz/F8lXYDsmQa29iPSt+kO+Jn7ArvRdq10g0CCk8wbRS0sb2zb6fvd9xL1BgR5UDQL771V0l8X32etvY5yKA==
|
integrity sha512-+j6JuMnFH421Z2vOxc+0YMt5Su5vD76RSatviy3zHBaZpgd+sOeAWoCLBHD5E7mMz5oKae3Y3wewCt9LRzq2Nw==
|
||||||
|
|
||||||
eslint-plugin-unicorn@^45.0.0:
|
eslint-plugin-unicorn@^45.0.0:
|
||||||
version "45.0.1"
|
version "45.0.1"
|
||||||
|
Loading…
Reference in New Issue
Block a user