From 53e7100033a9c9283f79bb3a4c5070a461709631 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 25 Jun 2021 14:35:58 +0100 Subject: [PATCH] Initial Typescripting for Element Desktop --- .gitignore | 1 + package.json | 13 +- src/@types/global.d.ts | 26 +++ src/{electron-main.js => electron-main.ts} | 47 ++-- ...{language-helper.js => language-helper.ts} | 51 ++-- src/protocol.js | 102 -------- src/protocol.ts | 103 ++++++++ src/{squirrelhooks.js => squirrelhooks.ts} | 14 +- src/{tray.js => tray.ts} | 37 +-- src/{updater.js => updater.ts} | 22 +- src/{vectormenu.js => vectormenu.ts} | 37 ++- ...ents-handler.js => webcontents-handler.ts} | 83 ++++--- tsconfig.json | 25 ++ yarn.lock | 220 +++++++++++++++++- 14 files changed, 543 insertions(+), 238 deletions(-) create mode 100644 src/@types/global.d.ts rename src/{electron-main.js => electron-main.ts} (97%) rename src/{language-helper.js => language-helper.ts} (76%) delete mode 100644 src/protocol.js create mode 100644 src/protocol.ts rename src/{squirrelhooks.js => squirrelhooks.ts} (93%) rename src/{tray.js => tray.ts} (86%) rename src/{updater.js => updater.ts} (92%) rename src/{vectormenu.js => vectormenu.ts} (89%) rename src/{webcontents-handler.js => webcontents-handler.ts} (75%) create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index a049439..8abadc3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /dist +/lib /webapp /webapp.asar /packages diff --git a/package.json b/package.json index 063c64f..94a2a80 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "element-desktop", "productName": "Element", - "main": "src/electron-main.js", + "main": "lib/electron-main.js", "version": "1.7.31", "description": "A feature-rich client for Matrix.org", "author": "Element", @@ -18,12 +18,13 @@ "mkdirs": "mkdirp packages deploys", "fetch": "yarn run mkdirs && node scripts/fetch-package.js", "asar-webapp": "asar p webapp webapp.asar", - "start": "electron .", + "start": "yarn run tsbuild && electron .", "lint": "eslint src/ scripts/ hak/", "build:native": "yarn run hak", "build32": "electron-builder --ia32", "build64": "electron-builder --x64", - "build": "electron-builder", + "build": "yarn run tsbuild && electron-builder", + "tsbuild": "tsc", "docker:setup": "docker build -t element-desktop-dockerbuild dockerbuild", "docker:build:native": "scripts/in-docker.sh yarn run hak", "docker:build": "scripts/in-docker.sh yarn run build", @@ -42,7 +43,10 @@ "request": "^2.88.2" }, "devDependencies": { + "@types/auto-launch": "^5.0.1", + "@types/counterpart": "^0.18.1", "asar": "^2.0.1", + "electron": "12.0.11", "electron-builder": "22.11.4", "electron-builder-squirrel-windows": "22.11.4", "electron-devtools-installer": "^3.1.1", @@ -60,7 +64,8 @@ "npm": "^6.14.11", "rimraf": "^3.0.2", "semver": "^7.3.4", - "tar": "^6.1.0" + "tar": "^6.1.0", + "typescript": "^4.1.3" }, "hakDependencies": { "matrix-seshat": "^2.2.3", diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts new file mode 100644 index 0000000..cc74a3a --- /dev/null +++ b/src/@types/global.d.ts @@ -0,0 +1,26 @@ +/* +Copyright 2021 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { BrowserWindow } from "electron"; + +declare global { + namespace NodeJS { + interface Global { + mainWindow: BrowserWindow; + appQuitting: boolean; + } + } +} \ No newline at end of file diff --git a/src/electron-main.js b/src/electron-main.ts similarity index 97% rename from src/electron-main.js rename to src/electron-main.ts index 049ab15..40997ed 100644 --- a/src/electron-main.js +++ b/src/electron-main.ts @@ -20,32 +20,29 @@ 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. -const checkSquirrelHooks = require('./squirrelhooks'); -if (checkSquirrelHooks()) return; +import { checkSquirrelHooks } from "./squirrelhooks"; +if (checkSquirrelHooks()) process.exit(1); const argv = require('minimist')(process.argv, { alias: { help: "h" }, }); -const { - app, ipcMain, powerSaveBlocker, BrowserWindow, Menu, autoUpdater, protocol, dialog, -} = require('electron'); -const AutoLaunch = require('auto-launch'); -const path = require('path'); +import { app, ipcMain, powerSaveBlocker, BrowserWindow, Menu, autoUpdater, protocol, dialog } from "electron"; +import AutoLaunch from "auto-launch"; +import path from "path"; +import windowStateKeeper from 'electron-window-state'; +import Store from 'electron-store'; +import fs, { promises as afs } from "fs"; +import crypto from "crypto"; +import { URL } from "url"; -const tray = require('./tray'); -const buildMenuTemplate = require('./vectormenu'); -const webContentsHandler = require('./webcontents-handler'); -const updater = require('./updater'); -const { getProfileFromDeeplink, protocolInit, recordSSOSession } = require('./protocol'); +import * as tray from "./tray"; +import { buildMenuTemplate } from './vectormenu'; +import webContentsHandler from './webcontents-handler'; +import * as updater from './updater'; +import { getProfileFromDeeplink, protocolInit, recordSSOSession } from './protocol'; +import { _t, AppLocalization } from './language-helper'; -const windowStateKeeper = require('electron-window-state'); -const Store = require('electron-store'); - -const fs = require('fs'); -const afs = fs.promises; - -const crypto = require('crypto'); let keytar; try { keytar = require('keytar'); @@ -57,8 +54,6 @@ try { } } -const { _t, AppLocalization } = require('./language-helper'); - let seshatSupported = false; let Seshat; let SeshatRecovery; @@ -259,7 +254,13 @@ async function moveAutoLauncher() { } const eventStorePath = path.join(app.getPath('userData'), 'EventStore'); -const store = new Store({ name: "electron-config" }); +const store = new Store<{ + warnBeforeExit?: boolean; + minimizeToTray?: boolean; + spellCheckerEnabled?: boolean; + autoHideMenuBar?: boolean; + locale?: string | string[]; +}>({ name: "electron-config" }); let eventIndex = null; @@ -1016,7 +1017,7 @@ function beforeQuit() { } app.on('before-quit', beforeQuit); -app.on('before-quit-for-update', beforeQuit); +autoUpdater.on('before-quit-for-update', beforeQuit); app.on('second-instance', (ev, commandLine, workingDirectory) => { // If other instance launched with --hidden then skip showing window diff --git a/src/language-helper.js b/src/language-helper.ts similarity index 76% rename from src/language-helper.js rename to src/language-helper.ts index 0a41f69..b4dcb70 100644 --- a/src/language-helper.js +++ b/src/language-helper.ts @@ -14,15 +14,23 @@ See the License for the specific language governing permissions and limitations under the License. */ -const counterpart = require('counterpart'); +import counterpart from "counterpart"; +import type Store from 'electron-store'; const DEFAULT_LOCALE = "en"; -function _td(text) { +export function _td(text: string): string { return text; } -function _t(text, variables = {}) { +type SubstitutionValue = number | string; + +interface IVariables { + [key: string]: SubstitutionValue; + count?: number; +} + +export function _t(text: string, variables: IVariables = {}): string { const args = Object.assign({ interpolate: false }, variables); const { count } = args; @@ -55,11 +63,17 @@ function _t(text, variables = {}) { return translated; } -class AppLocalization { - constructor({ store, components = [] }) { - // TODO: Should be static field, but that doesn't parse without Babel - this.STORE_KEY = "locale"; +type Component = () => void; +type TypedStore = Store<{ locale?: string | string[] }>; + +export class AppLocalization { + private static readonly STORE_KEY = "locale"; + + private readonly store: TypedStore; + private readonly localizedComponents: Set; + + constructor({ store, components = [] }: { store: TypedStore, components: Component[] }) { counterpart.registerTranslations("en", this.fetchTranslationJson("en_EN")); counterpart.setFallbackLocale('en'); counterpart.setSeparator('|'); @@ -69,15 +83,15 @@ class AppLocalization { } this.store = store; - if (this.store.has(this.STORE_KEY)) { - const locales = this.store.get(this.STORE_KEY); + if (this.store.has(AppLocalization.STORE_KEY)) { + const locales = this.store.get(AppLocalization.STORE_KEY); this.setAppLocale(locales); } this.resetLocalizedUI(); } - fetchTranslationJson(locale) { + public fetchTranslationJson(locale: string): Record { try { console.log("Fetching translation json for locale: " + locale); return require(`./i18n/strings/${locale}.json`); @@ -87,11 +101,7 @@ class AppLocalization { } } - get languageTranslationJson() { - return this.translationJsonMap.get(this.language); - } - - setAppLocale(locales) { + public setAppLocale(locales: string | string[]): void { console.log(`Changing application language to ${locales}`); if (!Array.isArray(locales)) { @@ -105,13 +115,14 @@ class AppLocalization { } }); + // @ts-ignore - this looks like a bug but is out of scope for this conversion counterpart.setLocale(locales); - this.store.set(this.STORE_KEY, locales); + this.store.set(AppLocalization.STORE_KEY, locales); this.resetLocalizedUI(); } - resetLocalizedUI() { + public resetLocalizedUI(): void { console.log("Resetting the UI components after locale change"); this.localizedComponents.forEach(componentSetup => { if (typeof componentSetup === "function") { @@ -120,9 +131,3 @@ class AppLocalization { }); } } - -module.exports = { - AppLocalization, - _t, - _td, -}; diff --git a/src/protocol.js b/src/protocol.js deleted file mode 100644 index 69f3df5..0000000 --- a/src/protocol.js +++ /dev/null @@ -1,102 +0,0 @@ -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -const { app } = require("electron"); -const path = require("path"); -const fs = require("fs"); - -const PROTOCOL = "element://"; -const SEARCH_PARAM = "element-desktop-ssoid"; -const STORE_FILE_NAME = "sso-sessions.json"; - -// we getPath userData before electron-main changes it, so this is the default value -const storePath = path.join(app.getPath("userData"), STORE_FILE_NAME); - -const processUrl = (url) => { - if (!global.mainWindow) return; - console.log("Handling link: ", url); - global.mainWindow.loadURL(url.replace(PROTOCOL, "vector://")); -}; - -const readStore = () => { - try { - const s = fs.readFileSync(storePath, { encoding: "utf8" }); - const o = JSON.parse(s); - return typeof o === "object" ? o : {}; - } catch (e) { - return {}; - } -}; - -const writeStore = (data) => { - fs.writeFileSync(storePath, JSON.stringify(data)); -}; - -module.exports = { - recordSSOSession: (sessionID) => { - const userDataPath = app.getPath('userData'); - const store = readStore(); - for (const key in store) { - // ensure each instance only has one (the latest) session ID to prevent the file growing unbounded - if (store[key] === userDataPath) { - delete store[key]; - break; - } - } - store[sessionID] = userDataPath; - writeStore(store); - }, - getProfileFromDeeplink: (args) => { - // check if we are passed a profile in the SSO callback url - const deeplinkUrl = args.find(arg => arg.startsWith('element://')); - if (deeplinkUrl && deeplinkUrl.includes(SEARCH_PARAM)) { - const parsedUrl = new URL(deeplinkUrl); - if (parsedUrl.protocol === 'element:') { - const ssoID = parsedUrl.searchParams.get(SEARCH_PARAM); - const store = readStore(); - console.log("Forwarding to profile: ", store[ssoID]); - return store[ssoID]; - } - } - }, - protocolInit: () => { - // 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 - // --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"); - if (app.isPackaged) { - app.setAsDefaultProtocolClient('element', process.execPath, args); - } 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 - app.setAsDefaultProtocolClient('element', process.execPath, [app.getAppPath(), ...args]); - } - - if (process.platform === 'darwin') { - // Protocol handler for macos - app.on('open-url', function(ev, url) { - ev.preventDefault(); - processUrl(url); - }); - } else { - // Protocol handler for win32/Linux - app.on('second-instance', (ev, commandLine) => { - const url = commandLine[commandLine.length - 1]; - if (!url.startsWith(PROTOCOL)) return; - processUrl(url); - }); - } - }, -}; diff --git a/src/protocol.ts b/src/protocol.ts new file mode 100644 index 0000000..5ffbc40 --- /dev/null +++ b/src/protocol.ts @@ -0,0 +1,103 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { app } from "electron"; +import { URL } from "url"; +import path from "path"; +import fs from "fs"; + +const PROTOCOL = "element://"; +const SEARCH_PARAM = "element-desktop-ssoid"; +const STORE_FILE_NAME = "sso-sessions.json"; + +// we getPath userData before electron-main changes it, so this is the default value +const storePath = path.join(app.getPath("userData"), STORE_FILE_NAME); + +function processUrl(url: string): void { + if (!global.mainWindow) return; + console.log("Handling link: ", url); + global.mainWindow.loadURL(url.replace(PROTOCOL, "vector://")); +} + +function readStore(): object { + try { + const s = fs.readFileSync(storePath, { encoding: "utf8" }); + const o = JSON.parse(s); + return typeof o === "object" ? o : {}; + } catch (e) { + return {}; + } +} + +function writeStore(data: object): void { + fs.writeFileSync(storePath, JSON.stringify(data)); +} + +export function recordSSOSession(sessionID: string): void { + const userDataPath = app.getPath('userData'); + const store = readStore(); + for (const key in store) { + // ensure each instance only has one (the latest) session ID to prevent the file growing unbounded + if (store[key] === userDataPath) { + delete store[key]; + break; + } + } + store[sessionID] = userDataPath; + writeStore(store); +} + +export function getProfileFromDeeplink(args): string | undefined { + // check if we are passed a profile in the SSO callback url + const deeplinkUrl = args.find(arg => arg.startsWith('element://')); + if (deeplinkUrl && deeplinkUrl.includes(SEARCH_PARAM)) { + const parsedUrl = new URL(deeplinkUrl); + if (parsedUrl.protocol === 'element:') { + const ssoID = parsedUrl.searchParams.get(SEARCH_PARAM); + const store = readStore(); + console.log("Forwarding to profile: ", store[ssoID]); + return store[ssoID]; + } + } +} + +export function protocolInit(): void { + // 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 + // --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"); + if (app.isPackaged) { + app.setAsDefaultProtocolClient('element', process.execPath, args); + } 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 + app.setAsDefaultProtocolClient('element', process.execPath, [app.getAppPath(), ...args]); + } + + if (process.platform === 'darwin') { + // Protocol handler for macos + app.on('open-url', function(ev, url) { + ev.preventDefault(); + processUrl(url); + }); + } else { + // Protocol handler for win32/Linux + app.on('second-instance', (ev, commandLine) => { + const url = commandLine[commandLine.length - 1]; + if (!url.startsWith(PROTOCOL)) return; + processUrl(url); + }); + } +} diff --git a/src/squirrelhooks.js b/src/squirrelhooks.ts similarity index 93% rename from src/squirrelhooks.js rename to src/squirrelhooks.ts index 4eeb1c8..9863312 100644 --- a/src/squirrelhooks.js +++ b/src/squirrelhooks.ts @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -const path = require('path'); -const spawn = require('child_process').spawn; -const { app } = require('electron'); -const fsProm = require('fs').promises; +import path from "path"; +import { spawn } from "child_process"; +import { app } from "electron"; +import { promises as fsProm } from "fs"; -function runUpdateExe(args) { +function runUpdateExe(args: string[]): Promise { // Invokes Squirrel's Update.exe which will do things for us like create shortcuts // 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 @@ -33,7 +33,7 @@ function runUpdateExe(args) { }); } -function checkSquirrelHooks() { +export function checkSquirrelHooks(): boolean { if (process.platform !== 'win32') return false; const cmd = process.argv[1]; @@ -82,5 +82,3 @@ function checkSquirrelHooks() { } return false; } - -module.exports = checkSquirrelHooks; diff --git a/src/tray.js b/src/tray.ts similarity index 86% rename from src/tray.js rename to src/tray.ts index 34ae072..f3c49f7 100644 --- a/src/tray.js +++ b/src/tray.ts @@ -15,26 +15,26 @@ See the License for the specific language governing permissions and limitations under the License. */ -const { app, Tray, Menu, nativeImage } = require('electron'); -const pngToIco = require('png-to-ico'); -const path = require('path'); -const fs = require('fs'); -const { _t } = require('./language-helper'); +import { app, Tray, Menu, nativeImage } from "electron"; +import pngToIco from "png-to-ico"; +import path from "path"; +import fs from "fs"; +import { _t } from "./language-helper"; -let trayIcon = null; +let trayIcon: Tray = null; -exports.hasTray = function hasTray() { +export function hasTray(): boolean { return (trayIcon !== null); -}; +} -exports.destroy = function() { +export function destroy(): void { if (trayIcon) { trayIcon.destroy(); trayIcon = null; } -}; +} -const toggleWin = function() { +function toggleWin(): void { if (global.mainWindow.isVisible() && !global.mainWindow.isMinimized()) { global.mainWindow.hide(); } else { @@ -42,9 +42,14 @@ const toggleWin = function() { if (!global.mainWindow.isVisible()) global.mainWindow.show(); global.mainWindow.focus(); } -}; +} -exports.create = function(config) { +interface IConfig { + icon_path: string; + brand: string; +} + +export function create(config: IConfig): void { // no trays on darwin if (process.platform === 'darwin' || trayIcon) return; const defaultIcon = nativeImage.createFromPath(config.icon_path); @@ -89,9 +94,9 @@ exports.create = function(config) { global.mainWindow.webContents.on('page-title-updated', function(ev, title) { trayIcon.setToolTip(title); }); -}; +} -function initApplicationMenu() { +export function initApplicationMenu(): void { if (!trayIcon) { return; } @@ -112,5 +117,3 @@ function initApplicationMenu() { trayIcon.setContextMenu(contextMenu); } - -exports.initApplicationMenu = initApplicationMenu; diff --git a/src/updater.js b/src/updater.ts similarity index 92% rename from src/updater.js rename to src/updater.ts index c1050d2..7ed55c5 100644 --- a/src/updater.js +++ b/src/updater.ts @@ -14,19 +14,19 @@ See the License for the specific language governing permissions and limitations under the License. */ -const { app, autoUpdater, ipcMain } = require('electron'); +import { app, autoUpdater, ipcMain } from "electron"; const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000; const INITIAL_UPDATE_DELAY_MS = 30 * 1000; -function installUpdate() { +function installUpdate(): void { // for some reason, quitAndInstall does not fire the // before-quit event, so we need to set the flag here. global.appQuitting = true; autoUpdater.quitAndInstall(); } -function pollForUpdates() { +function pollForUpdates(): void { try { autoUpdater.checkForUpdates(); } catch (e) { @@ -34,8 +34,7 @@ function pollForUpdates() { } } -module.exports = {}; -module.exports.start = function startAutoUpdate(updateBaseUrl) { +export function start(updateBaseUrl: string): void { if (updateBaseUrl.slice(-1) !== '/') { updateBaseUrl = updateBaseUrl + '/'; } @@ -80,18 +79,25 @@ module.exports.start = function startAutoUpdate(updateBaseUrl) { // will fail if running in debug mode console.log('Couldn\'t enable update checking', err); } -}; +} ipcMain.on('install_update', installUpdate); ipcMain.on('check_updates', pollForUpdates); -function ipcChannelSendUpdateStatus(status) { +function ipcChannelSendUpdateStatus(status: boolean | string): void { if (!global.mainWindow) return; global.mainWindow.webContents.send('check_updates', status); } +interface ICachedUpdate { + releaseNotes: string; + releaseName: string; + releaseDate: Date; + updateURL : string; +} + // cache the latest update which has been downloaded as electron offers no api to read it -let latestUpdateDownloaded; +let latestUpdateDownloaded: ICachedUpdate; autoUpdater.on('update-available', function() { ipcChannelSendUpdateStatus(true); }).on('update-not-available', function() { diff --git a/src/vectormenu.js b/src/vectormenu.ts similarity index 89% rename from src/vectormenu.js rename to src/vectormenu.ts index 674e346..8b6084b 100644 --- a/src/vectormenu.js +++ b/src/vectormenu.ts @@ -14,14 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -const { app, shell, Menu } = require('electron'); -const { _t } = require('./language-helper'); +import { app, shell, Menu, MenuItem, MenuItemConstructorOptions } from 'electron'; +import { _t } from './language-helper'; const isMac = process.platform === 'darwin'; -function buildMenuTemplate() { +export function buildMenuTemplate(): Menu { // Menu template from http://electron.atom.io/docs/api/menu/, edited - const template = [ + const template: Array<(MenuItemConstructorOptions) | (MenuItem)> = [ { label: _t('Edit'), accelerator: 'e', @@ -48,7 +48,7 @@ function buildMenuTemplate() { label: _t('Paste'), }, { - role: 'pasteandmatchstyle', + role: 'pasteAndMatchStyle', label: _t('Paste and Match Style'), }, { @@ -56,7 +56,7 @@ function buildMenuTemplate() { label: _t('Delete'), }, { - role: 'selectall', + role: 'selectAll', label: _t('Select All'), }, ], @@ -67,30 +67,30 @@ function buildMenuTemplate() { submenu: [ { type: 'separator' }, { - role: 'resetzoom', + role: 'resetZoom', accelerator: 'CmdOrCtrl+Num0', visible: false, }, { - role: 'zoomin', + role: 'zoomIn', accelerator: 'CmdOrCtrl+NumAdd', visible: false, }, { - role: 'zoomout', + role: 'zoomOut', accelerator: 'CmdOrCtrl+NumSub', visible: false, }, { - role: 'resetzoom', + role: 'resetZoom', label: _t('Actual Size'), }, { - role: 'zoomin', + role: 'zoomIn', label: _t('Zoom In'), }, { - role: 'zoomout', + role: 'zoomOut', label: _t('Zoom Out'), }, { type: 'separator' }, @@ -104,7 +104,7 @@ function buildMenuTemplate() { label: _t('Toggle Full Screen'), }, { - role: 'toggledevtools', + role: 'toggleDevTools', label: _t('Toggle Developer Tools'), }, ], @@ -166,7 +166,7 @@ function buildMenuTemplate() { label: _t('Hide'), }, { - role: 'hideothers', + role: 'hideOthers', label: _t('Hide Others'), }, { @@ -182,17 +182,17 @@ function buildMenuTemplate() { }); // Edit menu. // This has a 'speech' section on macOS - template[1].submenu.push( + (template[1].submenu as MenuItemConstructorOptions[]).push( { type: 'separator' }, { label: _t('Speech'), submenu: [ { - role: 'startspeaking', + role: 'startSpeaking', label: _t('Start Speaking'), }, { - role: 'stopspeaking', + role: 'stopSpeaking', label: _t('Stop Speaking'), }, ], @@ -243,6 +243,3 @@ function buildMenuTemplate() { return Menu.buildFromTemplate(template); } - -module.exports = buildMenuTemplate; - diff --git a/src/webcontents-handler.js b/src/webcontents-handler.ts similarity index 75% rename from src/webcontents-handler.js rename to src/webcontents-handler.ts index f209d94..2ca2b3b 100644 --- a/src/webcontents-handler.js +++ b/src/webcontents-handler.ts @@ -1,19 +1,49 @@ -const { clipboard, nativeImage, Menu, MenuItem, shell, dialog, ipcMain } = require('electron'); -const url = require('url'); -const fs = require('fs'); -const request = require('request'); -const path = require('path'); -const { _t } = require('./language-helper'); +/* +Copyright 2021 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { + clipboard, + nativeImage, + Menu, + MenuItem, + shell, + dialog, + ipcMain, + NativeImage, + WebContents, + ContextMenuParams, + DownloadItem, + MenuItemConstructorOptions, + IpcMainEvent, +} from 'electron'; +import url from 'url'; +import fs from 'fs'; +import request from 'request'; +import path from 'path'; +import { _t } from './language-helper'; const MAILTO_PREFIX = "mailto:"; -const PERMITTED_URL_SCHEMES = [ +const PERMITTED_URL_SCHEMES: string[] = [ 'http:', 'https:', MAILTO_PREFIX, ]; -function safeOpenURL(target) { +function safeOpenURL(target: string): void { // openExternal passes the target to open/start/xdg-open, // so put fairly stringent limits on what can be opened // (for instance, open /bin/sh does indeed open a terminal @@ -28,7 +58,7 @@ function safeOpenURL(target) { } } -function onWindowOrNavigate(ev, target) { +function onWindowOrNavigate(ev: Event, target: string): void { // always prevent the default: if something goes wrong, // we don't want to end up opening it in the electron // app, as we could end up opening any sort of random @@ -37,7 +67,7 @@ function onWindowOrNavigate(ev, target) { safeOpenURL(target); } -function writeNativeImage(filePath, img) { +function writeNativeImage(filePath: string, img: NativeImage): Promise { switch (filePath.split('.').pop().toLowerCase()) { case "jpg": case "jpeg": @@ -50,7 +80,7 @@ function writeNativeImage(filePath, img) { } } -function onLinkContextMenu(ev, params) { +function onLinkContextMenu(ev: Event, params: ContextMenuParams, webContents: WebContents): void { let url = params.linkURL || params.srcURL; if (url.startsWith('vector://vector/webapp')) { @@ -76,7 +106,7 @@ function onLinkContextMenu(ev, params) { label: _t('Copy image'), accelerator: 'c', click() { - ev.sender.copyImageAt(params.x, params.y); + webContents.copyImageAt(params.x, params.y); }, })); } @@ -140,8 +170,8 @@ function onLinkContextMenu(ev, params) { ev.preventDefault(); } -function _CutCopyPasteSelectContextMenus(params) { - const options = []; +function CutCopyPasteSelectContextMenus(params: ContextMenuParams): MenuItemConstructorOptions[] { + const options: MenuItemConstructorOptions[] = []; if (params.misspelledWord) { params.dictionarySuggestions.forEach(word => { @@ -180,10 +210,10 @@ function _CutCopyPasteSelectContextMenus(params) { accelerator: 'p', enabled: params.editFlags.canPaste, }, { - role: 'pasteandmatchstyle', + role: 'pasteAndMatchStyle', enabled: params.editFlags.canPaste, }, { - role: 'selectall', + role: 'selectAll', label: _t("Select All"), accelerator: 'a', enabled: params.editFlags.canSelectAll, @@ -192,7 +222,7 @@ function _CutCopyPasteSelectContextMenus(params) { } function onSelectedContextMenu(ev, params) { - const items = _CutCopyPasteSelectContextMenus(params); + const items = CutCopyPasteSelectContextMenus(params); const popupMenu = Menu.buildFromTemplate(items); // popup() requires an options object even for no options @@ -200,12 +230,13 @@ function onSelectedContextMenu(ev, params) { ev.preventDefault(); } -function onEditableContextMenu(ev, params) { - const items = [ +function onEditableContextMenu(ev: Event, params: ContextMenuParams) { + const items: MenuItemConstructorOptions[] = [ { role: 'undo' }, { role: 'redo', enabled: params.editFlags.canRedo }, { type: 'separator' }, - ].concat(_CutCopyPasteSelectContextMenus(params)); + ...CutCopyPasteSelectContextMenus(params), + ]; const popupMenu = Menu.buildFromTemplate(items); @@ -214,20 +245,20 @@ function onEditableContextMenu(ev, params) { ev.preventDefault(); } -ipcMain.on('userDownloadOpen', function(ev, { path }) { +ipcMain.on('userDownloadOpen', function(ev: IpcMainEvent, { path }) { shell.openPath(path); }); -module.exports = (webContents) => { +export default (webContents: WebContents): void => { webContents.on('new-window', onWindowOrNavigate); - webContents.on('will-navigate', (ev, target) => { + webContents.on('will-navigate', (ev: Event, target: string): void => { if (target.startsWith("vector://")) return; return onWindowOrNavigate(ev, target); }); - webContents.on('context-menu', function(ev, params) { + webContents.on('context-menu', function(ev: Event, params: ContextMenuParams): void { if (params.linkURL || params.srcURL) { - onLinkContextMenu(ev, params); + onLinkContextMenu(ev, params, webContents); } else if (params.selectionText) { onSelectedContextMenu(ev, params); } else if (params.isEditable) { @@ -235,7 +266,7 @@ module.exports = (webContents) => { } }); - webContents.session.on('will-download', (event, item) => { + webContents.session.on('will-download', (event: Event, item: DownloadItem): void => { item.once('done', (event, state) => { if (state === 'completed') { const savePath = item.getSavePath(); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2dedf3c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "module": "commonjs", + "moduleResolution": "node", + "target": "es2016", + "noImplicitAny": false, + "sourceMap": false, + "outDir": "./lib", + "declaration": true, + "types": [ + "node" + ], + "lib": [ + "es2019", + "dom" + ] + }, + "include": [ + "./src/**/*.ts" + ] +} diff --git a/yarn.lock b/yarn.lock index 4cb9b01..159e4a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -127,6 +127,22 @@ ajv "^6.12.0" ajv-keywords "^3.4.1" +"@electron/get@^1.0.1": + version "1.12.4" + resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.12.4.tgz#a5971113fc1bf8fa12a8789dc20152a7359f06ab" + integrity sha512-6nr9DbJPUR9Xujw6zD3y+rS95TyItEVM0NVjt1EehY2vUWfIgPiIPVHxCvaTS0xr2B+DRxovYVKbuOWqC35kjg== + dependencies: + debug "^4.1.1" + env-paths "^2.2.0" + fs-extra "^8.1.0" + got "^9.6.0" + progress "^2.0.3" + semver "^6.2.0" + sumchecker "^3.0.1" + optionalDependencies: + global-agent "^2.0.2" + global-tunnel-ng "^2.7.1" + "@electron/universal@1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@electron/universal/-/universal-1.0.5.tgz#b812340e4ef21da2b3ee77b2b4d35c9b86defe37" @@ -482,11 +498,21 @@ dependencies: defer-to-connect "^1.0.1" +"@types/auto-launch@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/auto-launch/-/auto-launch-5.0.1.tgz#388a047edc0e754d8e8978cbd9ed4672b36be2c4" + integrity sha512-+KQ+/koZ7sJXnf5cnCANofY6yXAdYJNEoVZEuWcwJfuWbUp9u6l09I7KhwD+ivU+cdz7JId4V5ukxscWtHdSuw== + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/counterpart@^0.18.1": + version "0.18.1" + resolved "https://registry.yarnpkg.com/@types/counterpart/-/counterpart-0.18.1.tgz#b1b784d9e54d9879f0a8cb12f2caedab65430fe8" + integrity sha512-PRuFlBBkvdDOtxlIASzTmkEFar+S66Ek48NVVTWMUjtJAdn5vyMSN8y6IZIoIymGpR36q2nZbIYazBWyFxL+IQ== + "@types/debug@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" @@ -522,6 +548,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.21.tgz#d934aacc22424fe9622ebf6857370c052eae464e" integrity sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A== +"@types/node@^14.6.2": + version "14.17.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.4.tgz#218712242446fc868d0e007af29a4408c7765bc0" + integrity sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A== + "@types/plist@^3.0.1": version "3.0.2" resolved "https://registry.yarnpkg.com/@types/plist/-/plist-3.0.2.tgz#61b3727bba0f5c462fe333542534a0c3e19ccb01" @@ -998,6 +1029,11 @@ bmp-js@^0.1.0: resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.1.0.tgz#e05a63f796a6c1ff25f4771ec7adadc148c07233" integrity sha1-4Fpj95amwf8l9Hcex62twUjAcjM= +boolean@^3.0.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.1.2.tgz#e30f210a26b02458482a8cc353ab06f262a780c2" + integrity sha512-YN6UmV0FfLlBVvRvNPx3pz5W/mUoYB24J4WSXOKP/OOJpi+Oq6WYqPaNTHzjI0QzwWtnvEd5CGYyQPgp1jFxnw== + boxen@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" @@ -1033,7 +1069,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: +buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= @@ -1433,7 +1469,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.5.0: +concat-stream@^1.5.0, concat-stream@^1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -1459,6 +1495,14 @@ conf@^7.1.2: pkg-up "^3.1.0" semver "^7.3.2" +config-chain@^1.1.11: + version "1.1.13" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + config-chain@^1.1.12: version "1.1.12" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" @@ -1508,6 +1552,11 @@ copy-concurrently@^1.0.0: rimraf "^2.5.4" run-queue "^1.0.0" +core-js@^3.6.5: + version "3.15.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.1.tgz#6c08ab88abdf56545045ccf5fd81f47f407e7f1a" + integrity sha512-h8VbZYnc9pDzueiS2610IULDkpFFPunHwIpl8yRwFahAEEdSpHlTy3h3z3rKq5h11CaUdBEeRViu9AYvbxiMeg== + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -1618,6 +1667,13 @@ debug@3.1.0: dependencies: ms "2.0.0" +debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + debug@^3.1.0: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -1729,6 +1785,11 @@ detect-newline@^2.1.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + dezalgo@^1.0.0, dezalgo@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -1928,6 +1989,15 @@ electron-window-state@^5.0.3: jsonfile "^4.0.0" mkdirp "^0.5.1" +electron@12.0.11: + version "12.0.11" + resolved "https://registry.yarnpkg.com/electron/-/electron-12.0.11.tgz#555dc1cf663e320f2f2cbdf89319352b08fc59f2" + integrity sha512-6gPjcce3QCeAWZ8UVAqVy6os+86D5BcxgkIzlROxX89u+du/i7WpZXF5F1vgv419rspds9OHejP3JrJnoUGh6w== + dependencies: + "@electron/get" "^1.0.1" + "@types/node" "^14.6.2" + extract-zip "^1.0.3" + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -1938,6 +2008,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +encodeurl@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + encoding@^0.1.11: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -2008,6 +2083,11 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es6-error@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -2035,6 +2115,11 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + eslint-config-google@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.14.0.tgz#4f5f8759ba6e11b424294a219dbfa18c508bcc1a" @@ -2200,6 +2285,16 @@ extend@^3.0.0, extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +extract-zip@^1.0.3: + version "1.7.0" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" + integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== + dependencies: + concat-stream "^1.6.2" + debug "^2.6.9" + mkdirp "^0.5.4" + yauzl "^2.10.0" + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -2230,6 +2325,13 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: version "3.5.2" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" @@ -2548,6 +2650,19 @@ glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +global-agent@^2.0.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.2.0.tgz#566331b0646e6bf79429a16877685c4a1fbf76dc" + integrity sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg== + dependencies: + boolean "^3.0.1" + core-js "^3.6.5" + es6-error "^4.1.1" + matcher "^3.0.0" + roarr "^2.15.3" + semver "^7.3.2" + serialize-error "^7.0.1" + global-dirs@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" @@ -2562,6 +2677,16 @@ global-dirs@^3.0.0: dependencies: ini "2.0.0" +global-tunnel-ng@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f" + integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg== + dependencies: + encodeurl "^1.0.2" + lodash "^4.17.10" + npm-conf "^1.1.3" + tunnel "^0.0.6" + global@~4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" @@ -2582,6 +2707,13 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globalthis@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" + integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ== + dependencies: + define-properties "^1.1.3" + got@^6.7.1: version "6.7.1" resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" @@ -3175,7 +3307,7 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= -json-stringify-safe@~5.0.1: +json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= @@ -3546,7 +3678,7 @@ lodash.without@~4.4.0: resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw= -lodash@^4.17.15, lodash@^4.17.20: +lodash@^4.17.10, lodash@^4.17.15, lodash@^4.17.20: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -3621,6 +3753,13 @@ make-fetch-happen@^5.0.0: socks-proxy-agent "^4.0.0" ssri "^6.0.0" +matcher@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" + integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== + dependencies: + escape-string-regexp "^4.0.0" + "matrix-web-i18n@github:matrix-org/matrix-web-i18n": version "1.1.2" resolved "https://codeload.github.com/matrix-org/matrix-web-i18n/tar.gz/63f9119bc0bc304e83d4e8e22364caa7850e7671" @@ -3765,7 +3904,7 @@ mkdirp@^0.5.0, mkdirp@~0.5.0: dependencies: minimist "0.0.8" -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5: +mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -3943,6 +4082,14 @@ npm-cache-filename@~1.0.2: resolved "https://registry.yarnpkg.com/npm-cache-filename/-/npm-cache-filename-1.0.2.tgz#ded306c5b0bfc870a9e9faf823bc5f283e05ae11" integrity sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE= +npm-conf@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" + integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== + dependencies: + config-chain "^1.1.11" + pify "^3.0.0" + npm-install-checks@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-3.0.2.tgz#ab2e32ad27baa46720706908e5b14c1852de44d9" @@ -4478,6 +4625,11 @@ path-type@^2.0.0: dependencies: pify "^2.0.0" +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -4570,7 +4722,7 @@ process@~0.5.1: resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8= -progress@^2.0.0: +progress@^2.0.0, progress@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -4984,6 +5136,18 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +roarr@^2.15.3: + version "2.15.4" + resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" + integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== + dependencies: + boolean "^3.0.1" + detect-node "^2.0.4" + globalthis "^1.0.1" + json-stringify-safe "^5.0.1" + semver-compare "^1.0.0" + sprintf-js "^1.1.2" + run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" @@ -5018,6 +5182,11 @@ sax@>=0.6.0, sax@^1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= + semver-diff@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" @@ -5061,6 +5230,13 @@ semver@^7.3.5: dependencies: lru-cache "^6.0.0" +serialize-error@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" + integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== + dependencies: + type-fest "^0.13.1" + set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -5211,7 +5387,7 @@ split-on-first@^1.0.0: resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== -sprintf-js@^1.0.3: +sprintf-js@^1.0.3, sprintf-js@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== @@ -5397,6 +5573,13 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +sumchecker@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" + integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== + dependencies: + debug "^4.1.0" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -5581,6 +5764,11 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -5593,6 +5781,11 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" +type-fest@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== + type-fest@^0.16.0: version "0.16.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860" @@ -5620,6 +5813,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typescript@^4.1.3: + version "4.3.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.4.tgz#3f85b986945bcf31071decdd96cf8bfa65f9dcbc" + integrity sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew== + uid-number@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" @@ -6114,6 +6312,14 @@ yargs@^8.0.2: y18n "^3.2.1" yargs-parser "^7.0.0" +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + zip-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79"