diff --git a/.eslintrc b/.eslintrc index f48be512..c793582c 100644 --- a/.eslintrc +++ b/.eslintrc @@ -100,6 +100,7 @@ "DiscordNative": "readonly", "__non_webpack_require__": "readonly", "Symbol": "readonly", - "__static": "readonly" + "__static": "readonly", + "status": "off" } } \ No newline at end of file diff --git a/assets/icon.icns b/assets/icon.icns new file mode 100644 index 00000000..4c4cbdb2 Binary files /dev/null and b/assets/icon.icns differ diff --git a/assets/icon.ico b/assets/icon.ico new file mode 100644 index 00000000..247cacea Binary files /dev/null and b/assets/icon.ico differ diff --git a/package.json b/package.json index dfdb0cef..154b8b33 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "betterdiscord", "productName": "BetterDiscord", - "description": "test", - "author": "Zerebos", + "description": "Installer for BetterDiscord.", + "author": "BetterDiscord", "version": "1.0.0", "license": "MIT", "scripts": { @@ -13,16 +13,17 @@ "dist:dir": "yarn dist --dir -c.compression=store -c.mac.identity=null" }, "dependencies": { - "bent": "^7.3.12", "source-map-support": "^0.5.16" }, "devDependencies": { + "del": "^6.0.0", "electron": "9.4.0", "electron-builder": "^22.4.1", "electron-webpack": "^2.8.2", "eslint": "^7.21.0", "eslint-plugin-svelte3": "^3.1.2", "find-process": "^1.4.4", + "phin": "^3.5.1", "svelte": "^3.35.0", "svelte-loader": "^3.0.0", "svelte-spa-router": "^3.1.0", @@ -32,11 +33,32 @@ "build": { "appId": "app.betterdiscord.installer", "productName": "BetterDiscord", + "copyright": "Copyright © 2021 BetterDiscord", + "afterAllArtifactBuild": "scripts/fixmac.js", + "win": { + "icon": "assets/icon.ico", + "target": { + "target": "portable", + "arch": ["ia32"] + } + }, "mac": { - "category": "your.app.category.type" + "icon": "assets/icon.icns", + "category": "public.app-category.social-networking", + "target": { + "target": "zip", + "arch": ["x64"] + } }, - "win": { - "target": "portable" + "linux": { + "category": "Utility", + "target": { + "target": "AppImage", + "arch": ["x64"] + } + }, + "appImage": { + "license": "assets/license.txt" } }, "electronWebpack": { diff --git a/scripts/fixmac.js b/scripts/fixmac.js new file mode 100644 index 00000000..fb434b06 --- /dev/null +++ b/scripts/fixmac.js @@ -0,0 +1,49 @@ +// Exists due to https://github.com/electron-userland/electron-builder/issues/4299 +// Tempfix adapted from: https://gist.github.com/harshitsilly/a1bd5a405f93966aad20358ae6c4cec5 + +const path = require("path"); +const {execSync} = require("child_process"); +const fs = require("fs"); +const yaml = require("js-yaml"); +const {appBuilderPath} = require("app-builder-bin"); +const currentWorkingDirectory = process.cwd(); +const packageInfo = require(path.join(currentWorkingDirectory, "package.json")); + +const APP_NAME = packageInfo.build.productName; +const APP_VERSION = process.argv[2] ? process.argv[2] : packageInfo.version; +const APP_DIST_PATH = path.join(currentWorkingDirectory, "dist"); + + +/* eslint-disable no-console */ +module.exports = function(buildResult) { + if (!buildResult.artifactPaths.some(p => p.endsWith("mac.zip"))) return console.log("No Mac build detected"); + console.log("Zipping Started"); + + execSync( + `ditto -c -k --sequesterRsrc --keepParent --zlibCompressionLevel 9 "${APP_DIST_PATH}/mac/${APP_NAME}.app" "${APP_DIST_PATH}/${APP_NAME}-${APP_VERSION}-mac.zip"` + ); + + console.log("Zipping Completed"); + + const APP_GENERATED_BINARY_PATH = path.join(APP_DIST_PATH, `${APP_NAME}-${APP_VERSION}-mac.zip`); + try { + const output = execSync( + `${appBuilderPath} blockmap --input="${APP_GENERATED_BINARY_PATH}" --output="${APP_DIST_PATH}/${APP_NAME}-${APP_VERSION}-mac.zip.blockmap" --compression=gzip` + ); + const {sha512, size} = JSON.parse(output); + + const ymlPath = path.join(APP_DIST_PATH, "latest-mac.yml"); + const ymlData = yaml.safeLoad(fs.readFileSync(ymlPath, "utf8")); + // console.log(ymlData); + ymlData.sha512 = sha512; + ymlData.files[0].sha512 = sha512; + ymlData.files[0].size = size; + const yamlStr = yaml.safeDump(ymlData); + // console.log(yamlStr); + fs.writeFileSync(ymlPath, yamlStr, "utf8"); + console.log("Successfully updated YAML file and configurations with blockmap."); + } + catch (e) { + console.log("Error in updating YAML file and configurations with blockmap.", e); + } +}; \ No newline at end of file diff --git a/src/renderer/App.svelte b/src/renderer/App.svelte index ef18deda..5bd1dc00 100644 --- a/src/renderer/App.svelte +++ b/src/renderer/App.svelte @@ -121,7 +121,7 @@ left: 0; width: 100%; height: 100%; - background-image: url('images/background.png'); + background-image: var(--background); background-size: 60px; background-repeat: repeat; background-position: center; diff --git a/src/renderer/actions/install.js b/src/renderer/actions/install.js index 788c833f..f49be1d2 100644 --- a/src/renderer/actions/install.js +++ b/src/renderer/actions/install.js @@ -1,138 +1,137 @@ import logs from "../stores/logs"; import {progress, status} from "../stores/installation"; import {remote, shell} from "electron"; -import fs from "fs"; +import {promises as fs} from "fs"; import path from "path"; -import bent from "bent"; -import kill from "tree-kill"; -import findProcess from "find-process"; - -const downloadFile = bent("buffer"); - -function log(entry) { - logs.update(a => { - a.push(entry); - return a; - }); -} - -let progressCache = 0; - -function addProgress(val) { - progressCache += val; - progress.set(progressCache); -} - -function failInstallation() { - const discordURL = "https://discord.gg/0Tmfo5ZbORCRqbAd"; - log(""); - log(`The installation seems to have failed. If this problem is recurring, join our discord community for support. ${discordURL}`); - status.set("error"); -} +import phin from "phin"; + +import {log, lognewline} from "./utils/log"; +import succeed from "./utils/succeed"; +import fail from "./utils/fail"; +import exists from "./utils/exists"; +import reset from "./utils/reset"; +import kill from "./utils/kill"; +import {showRestartNotice} from "./utils/notices"; +import doSanityCheck from "./utils/sanity"; + +const MAKE_DIR_PROGRESS = 30; +const DOWNLOAD_PACKAGE_PROGRESS = 60; +const INJECT_SHIM_PROGRESS = 90; +const RESTART_DISCORD_PROGRESS = 100; const bdFolder = path.join(remote.app.getPath("appData"), "BetterDiscord"); const bdDataFolder = path.join(bdFolder, "data"); const bdPluginsFolder = path.join(bdFolder, "plugins"); const bdThemesFolder = path.join(bdFolder, "themes"); -async function makeDirectories() { - const folders = [bdFolder, bdDataFolder, bdThemesFolder, bdPluginsFolder]; +async function makeDirectories(...folders) { + const progressPerLoop = (MAKE_DIR_PROGRESS - progress.value) / folders.length; for (const folder of folders) { - if (fs.existsSync(folder)) { + if (await exists(folder)) { log(`✅ Directory exists: ${folder}`); + progress.set(progress.value + progressPerLoop); continue; } try { - fs.mkdirSync(folder); + await fs.mkdir(folder); + progress.set(progress.value + progressPerLoop); log(`✅ Directory created: ${folder}`); } - catch { + catch (err) { log(`❌ Failed to create directory: ${folder}`); - failInstallation(); - return; + log(`❌ ${err.message}`); + return err; } } - progress.set(25); } -const downloadUrl = `https://bd.zerebos.com/betterdiscord.asar`; +const getJSON = phin.defaults({method: "GET", parse: "json", headers: {"User-Agent": "BetterDiscord Installer"}}); +const downloadFile = phin.defaults({method: "GET", followRedirects: true, headers: {"User-Agent": "BetterDiscord Installer", "Accept": "application/octet-stream"}}); const asarPath = path.join(bdDataFolder, "betterdiscord.asar"); async function downloadAsar() { - const buffer = await downloadFile(downloadUrl); - const originalFs = require("original-fs"); // because electron doesn't like when I write asar files - originalFs.writeFileSync(asarPath, buffer); -} - -async function restartDiscord() { - const results = await findProcess("name", "Discord", true); - if (!results || !results.length) return; - const parentPids = results.map(p => p.ppid); - const discordPid = results.find(p => parentPids.includes(p.pid)); - const bin = discordPid.bin; - kill(discordPid.pid); - shell.openExternal(bin); -} - - -export default async function(discordPaths) { - progress.set(0); - - log("Starting installation..."); - log(""); - log("Locating Discord paths..."); - - if (!discordPaths || !discordPaths.length) { - log("❌ Failed to locate required directories."); - failInstallation(); - return; - } - + let downloadUrl = "https://api.github.com/repos/rauenzi/BetterDiscordApp/releases"; try { - await makeDirectories(); + const response = await getJSON(downloadUrl); + const releases = response.body; + const asset = releases[0].assets.find(a => a.name === "betterdiscord.asar"); + downloadUrl = asset.url; + + const resp = await downloadFile(downloadUrl); + const originalFs = require("original-fs").promises; // because electron doesn't like when I write asar files + await originalFs.writeFile(asarPath, resp.body); } catch (err) { - log(`❌ Failed to create directories - ${err.message}`); - failInstallation(); - return; - } - - - log(""); - log(`Downloading asar file from: ${downloadUrl}`); - - try { - await downloadAsar(); - log("✅ Package downloaded"); - progress.set(50); - } - catch (err) { - log(`❌ Failed to download package - ${err.message}`); - failInstallation(); - return; + log(`❌ Failed to download package ${downloadUrl}`); + log(`❌ ${err.message}`); + return err; } +} - log(""); - log("Injecting shims..."); - for (const discordPath of discordPaths) { +async function injectShims(paths) { + const progressPerLoop = (INJECT_SHIM_PROGRESS - progress.value) / paths.length; + for (const discordPath of paths) { log("Injecting into: " + discordPath); const appPath = path.join(discordPath, "app"); const pkgFile = path.join(appPath, "package.json"); const indexFile = path.join(appPath, "index.js"); try { - if (!fs.existsSync(appPath)) fs.mkdirSync(appPath); - fs.writeFileSync(pkgFile, JSON.stringify({name: "betterdiscord", main: "index.js"})); - fs.writeFileSync(indexFile, `require("${asarPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}");`); + if (process.platform === "win32" || process.platform === "darwin") { + if (!(await exists(appPath))) await fs.mkdir(appPath); + await fs.writeFile(pkgFile, JSON.stringify({name: "betterdiscord", main: "index.js"})); + await fs.writeFile(indexFile, `require("${asarPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}");`); + } + else { + await fs.writeFile(path.join(discordPath, "index.js"), `require("${asarPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}");\nmodule.exports = require("./core.asar");`); + } log("✅ Injection successful"); + progress.set(progress.value + progressPerLoop); } catch (err) { - log(`❌ Injection Error - ${err.message}`); - failInstallation(); - return; + log(`❌ Could not inject shims to ${discordPath}`); + log(`❌ ${err.message}`); + return err; } } +} + + +export default async function(config) { + await reset(); + const sane = doSanityCheck(config); + if (!sane) return fail(); + + + const channels = Object.keys(config); + const paths = Object.values(config); + + + lognewline("Creating required directories..."); + const makeDirErr = await makeDirectories(bdFolder, bdDataFolder, bdThemesFolder, bdPluginsFolder); + if (makeDirErr) return fail(); + log("✅ Directories created"); + progress.set(MAKE_DIR_PROGRESS); + + + lognewline("Downloading asar file"); + const downloadErr = await downloadAsar(); + if (downloadErr) return fail(); + log("✅ Package downloaded"); + progress.set(DOWNLOAD_PACKAGE_PROGRESS); + + + lognewline("Injecting shims..."); + const injectErr = await injectShims(paths); + if (injectErr) return fail(); + log("✅ Shims injected"); + progress.set(INJECT_SHIM_PROGRESS); + + + lognewline("Restarting Discord..."); + const killErr = await kill(channels, (RESTART_DISCORD_PROGRESS - progress.value) / channels.length); + if (killErr) showRestartNotice(); // No need to bail out and show failed + else log("✅ Discord restarted"); + progress.set(RESTART_DISCORD_PROGRESS); - log("Installation completed!"); - progress.set(100); - status.set("success"); + succeed(); }; \ No newline at end of file diff --git a/src/renderer/actions/paths.js b/src/renderer/actions/paths.js index 9c5f1d5f..cdccdb70 100644 --- a/src/renderer/actions/paths.js +++ b/src/renderer/actions/paths.js @@ -1,5 +1,6 @@ const fs = require("fs"); const path = require("path"); +import {remote} from "electron"; export const platforms = {stable: "Discord", ptb: "Discord PTB", canary: "Discord Canary"}; export const locations = {stable: "", ptb: "", canary: ""}; @@ -17,7 +18,11 @@ const getDiscordPath = function(releaseChannel) { resourcePath = path.join("/Applications", `${releaseChannel}.app`, "Contents", "Resources"); } else { - resourcePath = path.join("/usr", "share", releaseChannel.toLowerCase().replace(/ /g, "-"), "resources"); + const basedir = path.join(remote.app.getPath("userData"), "..", releaseChannel.toLowerCase().replace(" ", "")); + if (!fs.existsSync(basedir)) return ""; + const version = fs.readdirSync(basedir).filter(f => fs.lstatSync(path.join(basedir, f)).isDirectory() && f.split(".").length > 1).sort().reverse()[0]; + if (!version) return ""; + resourcePath = path.join(basedir, version, "modules", "discord_desktop_core"); } if (fs.existsSync(resourcePath)) return resourcePath; @@ -31,7 +36,7 @@ for (const channel in platforms) { export const getBrowsePath = function(channel) { if (process.platform === "win32") return path.join(process.env.LOCALAPPDATA, platforms[channel].replace(" ", "")); else if (process.platform === "darwin") return path.join("/Applications", `${platforms[channel]}.app`); - return path.join("/usr", "share", platforms[channel].toLowerCase().replace(" ", "-")); + return path.join(remote.app.getPath("userData"), "..", platforms[channel].toLowerCase().replace(" ", "")); }; export const validatePath = function(channel, proposedPath) { @@ -76,14 +81,24 @@ const validateMac = function(channel, proposedPath) { }; const validateLinux = function(channel, proposedPath) { - const channelName = platforms[channel].toLowerCase().replace(" ", "-"); + if (proposedPath.includes("/snap/")) { + remote.dialog.showErrorBox("BetterDiscord Incompatible", "BetterDiscord is currently incompatible with Snap installs of Discord. Support for snap installs is coming soon!"); + return ""; + } + const channelName = platforms[channel].toLowerCase().replace(" ", ""); let resourcePath = ""; const selected = path.basename(proposedPath); - if (selected === channelName) resourcePath = path.join(proposedPath, "resources"); - if (selected === "resources") resourcePath = proposedPath; + if (selected === channelName) { + const version = fs.readdirSync(proposedPath).filter(f => fs.lstatSync(path.join(proposedPath, f)).isDirectory() && f.split(".").length > 1).sort().reverse()[0]; + if (!version) return ""; + resourcePath = path.join(proposedPath, version, "modules", "discord_desktop_core"); + } + if (selected.split(".").length > 2) resourcePath = path.join(proposedPath, "modules", "discord_desktop_core"); + if (selected === "modules") resourcePath = path.join(proposedPath, "discord_desktop_core"); + if (selected === "discord_desktop_core") resourcePath = proposedPath; - const executablePath = path.join(resourcePath, "..", "MacOS", platforms[channel]); - if (fs.existsSync(executablePath)) return resourcePath; + const asarPath = path.join(resourcePath, "core.asar"); + if (fs.existsSync(asarPath)) return resourcePath; return ""; }; diff --git a/src/renderer/actions/repair.js b/src/renderer/actions/repair.js new file mode 100644 index 00000000..74b6471c --- /dev/null +++ b/src/renderer/actions/repair.js @@ -0,0 +1,118 @@ + +import {progress, status} from "../stores/installation"; +import {remote} from "electron"; +import {promises as fs} from "fs"; +import del from "del"; +import path from "path"; +import install from "./install.js"; +import {log, lognewline} from "./utils/log"; +import succeed from "./utils/succeed"; +import fail from "./utils/fail"; +import exists from "./utils/exists"; +import kill from "./utils/kill"; +import reset from "./utils/reset"; +import {showKillNotice} from "./utils/notices"; +import doSanityCheck from "./utils/sanity"; + +const KILL_DISCORD_PROGRESS = 20; +const DELETE_APP_DIRS_PROGRESS = 50; +const DELETE_MODULE_DIRS_PROGRESS = 100; + +async function deleteAppDirs(paths) { + const progressPerLoop = (DELETE_APP_DIRS_PROGRESS - progress.value) / paths.length; + for (const discordPath of paths) { + log("Removing " + discordPath); + const appPath = path.join(discordPath, "app"); + try { + if (await exists(appPath)) await del(appPath, {force: true}); + log("✅ Deletion successful"); + progress.set(progress.value + progressPerLoop); + } + catch (err) { + log(` Could not delete folder ${appPath}`); + log(`❌ ${err.message}`); + return err; + } + } +} + +const platforms = {stable: "Discord", ptb: "Discord PTB", canary: "Discord Canary"}; +async function deleteModuleDirs(config) { + const size = Object.keys(config).length; + const progressPerLoop = (DELETE_MODULE_DIRS_PROGRESS - progress.value) / size; + for (const channel in config) { + const roaming = path.join(remote.app.getPath("userData"), "..", platforms[channel].replace(" ", "").toLowerCase()); + try { + const versionDir = (await fs.readdir(roaming)).find(d => d.split(".").length > 2); + if (await exists(path.join(versionDir, "modules"))) await del(versionDir, {force: true}); + log("✅ Deletion successful"); + progress.set(progress.value + progressPerLoop); + } + catch (err) { + log(`❌ Could not delete modules in ${roaming}`); + log(`❌ ${err.message}`); + return err; + } + } +} + +async function showInstallNotice(config) { + const confirmation = await remote.dialog.showMessageBox(remote.BrowserWindow.getFocusedWindow(), { + type: "question", + title: "Reinstall BetterDiscord?", + message: "After repairing, you need to reinstall BetterDiscord. Would you like to do that now?", + noLink: true, + cancelId: 1, + buttons: ["Yes", "No"] + }); + + if (confirmation.response !== 0) return succeed(); + + await reset(); + await install(config); + remote.dialog.showMessageBox({ + type: "info", + title: "Reinstall Complete", + message: "Please relaunch discord manually to finish the repair." + }); +} + + +export default async function(config) { + await reset(); + const sane = doSanityCheck(config); + if (!sane) return fail(); + + + const channels = Object.keys(config); + const paths = Object.values(config); + + + lognewline("Killing Discord..."); + const killErr = await kill(channels, (KILL_DISCORD_PROGRESS - progress.value) / channels.length, false); // await killProcesses(channels); + if (killErr) { + showKillNotice(); + return fail(); + } + log("✅ Discord Killed"); + progress.set(KILL_DISCORD_PROGRESS); + + + await new Promise(r => setTimeout(r, 200)); + lognewline("Deleting shims..."); + const deleteShimErr = await deleteAppDirs(paths); + if (deleteShimErr) return fail(); + log("✅ Shims deleted"); + progress.set(DELETE_APP_DIRS_PROGRESS); + + + await new Promise(r => setTimeout(r, 200)); + lognewline("Deleting discord modules..."); + const deleteModulesErr = await deleteModuleDirs(config); + if (deleteModulesErr) return fail(); + log("✅ Shims deleted"); + progress.set(DELETE_MODULE_DIRS_PROGRESS); + + + showInstallNotice(config); +}; \ No newline at end of file diff --git a/src/renderer/actions/uninstall.js b/src/renderer/actions/uninstall.js new file mode 100644 index 00000000..1e430ad3 --- /dev/null +++ b/src/renderer/actions/uninstall.js @@ -0,0 +1,70 @@ +import {promises as fs} from "fs"; +import del from "del"; +import path from "path"; + +import {progress} from "../stores/installation"; + +import {log, lognewline} from "./utils/log"; +import succeed from "./utils/succeed"; +import fail from "./utils/fail"; +import exists from "./utils/exists"; +import reset from "./utils/reset"; +import kill from "./utils/kill"; +import {showRestartNotice} from "./utils/notices"; +import doSanityCheck from "./utils/sanity"; + + +const DELETE_SHIM_PROGRESS = 85; +const RESTART_DISCORD_PROGRESS = 100; + + +async function deleteShims(paths) { + const progressPerLoop = (DELETE_SHIM_PROGRESS - progress.value) / paths.length; + for (const discordPath of paths) { + log("Removing " + discordPath); + const appPath = path.join(discordPath, "app"); + const indexFile = path.join(discordPath, "index.js"); + try { + if (process.platform === "win32" || process.platform === "darwin") { + if (await exists(appPath)) await del(appPath, {force: true}); + } + else { + if (await exists(indexFile)) await fs.writeFile(indexFile, `module.exports = require("./core.asar");`); + } + log("✅ Deletion successful"); + progress.set(progress.value + progressPerLoop); + } + catch (err) { + log(`❌ Could not delete folder ${appPath}`); + log(`❌ ${err.message}`); + return err; + } + } +} + + +export default async function(config) { + await reset(); + const sane = doSanityCheck(config); + if (!sane) return fail(); + + + const channels = Object.keys(config); + const paths = Object.values(config); + + + lognewline("Deleting shims..."); + const deleteErr = await deleteShims(paths); + if (deleteErr) return fail(); + log("✅ Shims deleted"); + progress.set(DELETE_SHIM_PROGRESS); + + + lognewline("Killing Discord..."); + const killErr = await kill(channels, (RESTART_DISCORD_PROGRESS - progress.value) / channels.length); + if (killErr) showRestartNotice(); // No need to bail out + else log("✅ Discord restarted"); + progress.set(RESTART_DISCORD_PROGRESS); + + succeed(); +}; \ No newline at end of file diff --git a/src/renderer/actions/utils/exists.js b/src/renderer/actions/utils/exists.js new file mode 100644 index 00000000..f37c244e --- /dev/null +++ b/src/renderer/actions/utils/exists.js @@ -0,0 +1,11 @@ +import {promises as fs} from "fs"; + +export default async function exists(file) { + try { + await fs.stat(file); + return true; + } + catch { + return false; + } +} \ No newline at end of file diff --git a/src/renderer/actions/utils/fail.js b/src/renderer/actions/utils/fail.js new file mode 100644 index 00000000..ec01a5e9 --- /dev/null +++ b/src/renderer/actions/utils/fail.js @@ -0,0 +1,10 @@ +import {log} from "./log"; +import {action, status} from "../../stores/installation"; + +const discordURL = "https://discord.gg/0Tmfo5ZbORCRqbAd"; + +export default function fail() { + log(""); + log(`The ${action.value} seems to have failed. If this problem is recurring, join our discord community for support. ${discordURL}`); + status.set("error"); +} \ No newline at end of file diff --git a/src/renderer/actions/utils/kill.js b/src/renderer/actions/utils/kill.js new file mode 100644 index 00000000..b8540428 --- /dev/null +++ b/src/renderer/actions/utils/kill.js @@ -0,0 +1,37 @@ +import findProcess from "find-process"; +import kill from "tree-kill"; +import {shell} from "electron"; +import {progress} from "../../stores/installation"; +import {log} from "./log"; + +const platforms = {stable: "Discord", ptb: "Discord PTB", canary: "Discord Canary"}; +export default async function killProcesses(channels, progressPerLoop, shouldRestart = true) { + for (const channel of channels) { + let processName = platforms[channel]; + if (process.platform === "darwin") processName = platforms[channel]; // Discord Canary and Discord PTB on Mac + else processName = platforms[channel].replace(" ", ""); // DiscordCanary and DiscordPTB on Windows/Linux + + log("Attempting to kill " + processName); + try { + const results = await findProcess("name", processName, true); + if (!results || !results.length) { + log(`✅ ${processName} not running`); + progress.set(progress.value + progressPerLoop); + continue; + } + + const parentPids = results.map(p => p.ppid); + const discordPid = results.find(p => parentPids.includes(p.pid)); + const bin = discordPid.bin; + kill(discordPid.pid); + if (shouldRestart) shell.openExternal(bin); + progress.set(progress.value + progressPerLoop); + } + catch (err) { + const symbol = shouldRestart ? "⚠️" : "❌"; + log(`${symbol} Could not kill ${platforms[channel]}`); + log(`${symbol} ${err.message}`); + return err; + } + } +} \ No newline at end of file diff --git a/src/renderer/actions/utils/log.js b/src/renderer/actions/utils/log.js new file mode 100644 index 00000000..a4360e3a --- /dev/null +++ b/src/renderer/actions/utils/log.js @@ -0,0 +1,16 @@ +import logs from "../../stores/logs"; + +export function log(entry) { + logs.update(a => { + a.push(entry); + return a; + }); +} + +export function lognewline(entry) { + logs.update(a => { + a.push(""); + a.push(entry); + return a; + }); +} \ No newline at end of file diff --git a/src/renderer/actions/utils/notices.js b/src/renderer/actions/utils/notices.js new file mode 100644 index 00000000..5ad17f7f --- /dev/null +++ b/src/renderer/actions/utils/notices.js @@ -0,0 +1,17 @@ +import {remote} from "electron"; + +export function showRestartNotice() { + remote.dialog.showMessageBox({ + type: "info", + title: "Restart Discord", + message: "BetterDiscord could not restart Discord. Please restart it manually." + }); +} + +export function showKillNotice() { + remote.dialog.showMessageBox({ + type: "error", + title: "Shutdown Discord", + message: "BetterDiscord could not shut down Discord. Please make sure Discord is fully closed, then run the installer again." + }); +} \ No newline at end of file diff --git a/src/renderer/actions/utils/reset.js b/src/renderer/actions/utils/reset.js new file mode 100644 index 00000000..908ed113 --- /dev/null +++ b/src/renderer/actions/utils/reset.js @@ -0,0 +1,9 @@ +import logs from "../../stores/logs"; +import {progress, status} from "../../stores/installation"; + +export default async function reset() { + logs.set([]); + progress.set(0); + status.set(""); + await new Promise(r => setTimeout(r, 500)); +} \ No newline at end of file diff --git a/src/renderer/actions/utils/sanity.js b/src/renderer/actions/utils/sanity.js new file mode 100644 index 00000000..18bca7ed --- /dev/null +++ b/src/renderer/actions/utils/sanity.js @@ -0,0 +1,14 @@ +import {log} from "./log"; +import {action} from "../../stores/installation"; + +export default function doSanityCheck(config) { + const paths = Object.values(config); + if (paths && paths.length) { + const name = action.value; + log(`Starting ${name.charAt(0).toUpperCase() + name.slice(1)}...`); + return true; + } + + log("❌ Something went wrong internally."); + return false; +} \ No newline at end of file diff --git a/src/renderer/actions/utils/succeed.js b/src/renderer/actions/utils/succeed.js new file mode 100644 index 00000000..4c02868a --- /dev/null +++ b/src/renderer/actions/utils/succeed.js @@ -0,0 +1,10 @@ +import {log} from "./log"; +import {action, progress, status} from "../../stores/installation"; + +export default function succeed() { + const name = action.value; + log(""); + log(`${name.charAt(0).toUpperCase() + name.slice(1)} completed!`); + progress.set(100); + status.set("success"); +} \ No newline at end of file diff --git a/src/renderer/common/TextDisplay.svelte b/src/renderer/common/TextDisplay.svelte index 16b65d72..d8f80194 100644 --- a/src/renderer/common/TextDisplay.svelte +++ b/src/renderer/common/TextDisplay.svelte @@ -40,7 +40,7 @@ try { setImmediate(() => scroller.scrollTop = scroller.scrollHeight); } - catch(e) {} + catch (e) {} } diff --git a/src/renderer/getstatic.js b/src/renderer/getstatic.js new file mode 100644 index 00000000..f61d1178 --- /dev/null +++ b/src/renderer/getstatic.js @@ -0,0 +1,11 @@ +import path from "path"; +import * as url from "url"; + +const isDevelopment = process.env.NODE_ENV !== "production"; + +export default function getStatic(val) { + if (isDevelopment) { + return url.resolve(window.location.origin, val); + } + return path.resolve(__static, val); +} \ No newline at end of file diff --git a/src/renderer/index.js b/src/renderer/index.js index 5d08c69e..7ca354bc 100644 --- a/src/renderer/index.js +++ b/src/renderer/index.js @@ -1,9 +1,14 @@ import App from "./App.svelte"; +import getStatic from "./getstatic"; +const appElement = document.getElementById("app"); const app = new App({ - target: document.getElementById("app") + target: appElement }); +// Setup this in a var because otherwise it won't work in prod +appElement.style.setProperty("--background", `url('${getStatic("/images/background.png")}')`); + window.refresh = () => window.location.href = `http://${window.location.host}/`; diff --git a/src/renderer/pages/License.svelte b/src/renderer/pages/License.svelte index 6f67551f..5b9bb318 100644 --- a/src/renderer/pages/License.svelte +++ b/src/renderer/pages/License.svelte @@ -38,6 +38,7 @@
+ diff --git a/src/renderer/pages/PerformAction.svelte b/src/renderer/pages/PerformAction.svelte index bd83d2fe..8ce9abb7 100644 --- a/src/renderer/pages/PerformAction.svelte +++ b/src/renderer/pages/PerformAction.svelte @@ -5,6 +5,8 @@ import TextDisplay from "../common/TextDisplay.svelte"; import logs from "../stores/logs"; import install from "../actions/install"; + import repair from "../actions/repair"; + import uninstall from "../actions/uninstall"; import debug from "../actions/debug"; import {canGoBack, canGoForward, nextPage} from "../stores/navigation"; import {action, paths, progress, platforms, status} from "../stores/installation"; @@ -32,17 +34,17 @@ // Run action scripts if (currentAction == "install") { - install(Object.values(installPaths)).then(() => { + pageIcon = ``; + install(installPaths).then(() => { nextPage.set(null); canGoForward.set(true); canGoBack.set(true); }); - pageIcon = ``; } else if (currentAction == "repair") { pageIcon = ``; // TODO: Finish repair script - debug(Object.values(installPaths)).then(() => { + repair(installPaths).then(() => { nextPage.set(null); canGoForward.set(true); canGoBack.set(true); @@ -50,8 +52,7 @@ } else if (currentAction == "uninstall") { pageIcon = ``; - // TODO: Finish uninstall script - debug(Object.values(installPaths)).then(() => { + uninstall(installPaths).then(() => { nextPage.set(null); canGoForward.set(true); canGoBack.set(true); diff --git a/src/renderer/pages/Platforms.svelte b/src/renderer/pages/Platforms.svelte index c3ba7775..3a11ec12 100644 --- a/src/renderer/pages/Platforms.svelte +++ b/src/renderer/pages/Platforms.svelte @@ -6,6 +6,7 @@ import {action, platforms, paths} from "../stores/installation"; import {platforms as platformLabels, validatePath, getBrowsePath} from "../actions/paths"; import {remote} from "electron"; + import getStatic from "../getstatic"; if (Object.values($platforms).some(r => r)) canGoForward.set(true); else canGoForward.set(false); @@ -50,7 +51,7 @@ {#each Object.entries(platformLabels) as [channel, label]} - Platform Icon + Platform Icon {label} {/each} diff --git a/src/renderer/stores/installation.js b/src/renderer/stores/installation.js index fbed09d9..273eb889 100644 --- a/src/renderer/stores/installation.js +++ b/src/renderer/stores/installation.js @@ -1,9 +1,11 @@ import {writable} from "svelte/store"; import {locations} from "../actions/paths"; +import readwritable from "./types/readwritable"; export const status = writable(""); export const hasAgreed = writable(false); -export const action = writable("install"); export const platforms = writable({stable: false, canary: false, ptb: false}); export const paths = writable({stable: locations.stable, canary: locations.canary, ptb: locations.ptb}); -export const progress = writable(0); \ No newline at end of file + +export const progress = readwritable(0); +export const action = readwritable("install"); \ No newline at end of file diff --git a/src/renderer/stores/types/readwritable.js b/src/renderer/stores/types/readwritable.js new file mode 100644 index 00000000..da0be003 --- /dev/null +++ b/src/renderer/stores/types/readwritable.js @@ -0,0 +1,22 @@ +import {writable} from "svelte/store"; + +export default function readWritable(initial) { + const {subscribe, set, update} = writable(initial); + + let cached = initial; + return { + subscribe, + update: fn => { + update(v => { + const retVal = fn(v); + cached = retVal; + return retVal; + }); + }, + set: val => { + cached = val; + set(val); + }, + get value() {return cached;} + }; +} \ No newline at end of file diff --git a/webpack.renderer.js b/webpack.renderer.js index f321424b..c0497e1a 100644 --- a/webpack.renderer.js +++ b/webpack.renderer.js @@ -3,7 +3,7 @@ module.exports = { rules: [ { test: /\.(html|svelte)$/, - use: 'svelte-loader' + use: "svelte-loader" } ] } diff --git a/yarn.lock b/yarn.lock index dd9d4b01..e15afaaf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -829,6 +829,27 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@nodelib/fs.scandir@2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" + integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA== + dependencies: + "@nodelib/fs.stat" "2.0.4" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655" + integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063" + integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow== + dependencies: + "@nodelib/fs.scandir" "2.1.4" + fastq "^1.6.0" + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -1320,6 +1341,11 @@ array-union@^1.0.1: dependencies: array-uniq "^1.0.1" +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + array-uniq@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" @@ -1442,15 +1468,6 @@ batch@0.6.1: resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= -bent@^7.3.12: - version "7.3.12" - resolved "https://registry.yarnpkg.com/bent/-/bent-7.3.12.tgz#e0a2775d4425e7674c64b78b242af4f49da6b035" - integrity sha512-T3yrKnVGB63zRuoco/7Ybl7BwwGZR0lceoVG5XmQyMIH9s19SV5m+a8qam4if0zQuAmOQTyPTPmsQBdAorGK3w== - dependencies: - bytesish "^0.4.1" - caseless "~0.12.0" - is-stream "^2.0.0" - big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -1561,6 +1578,13 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -1707,11 +1731,6 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -bytesish@^0.4.1: - version "0.4.4" - resolved "https://registry.yarnpkg.com/bytesish/-/bytesish-0.4.4.tgz#f3b535a0f1153747427aee27256748cff92347e6" - integrity sha512-i4uu6M4zuMUiyfZN4RU2+i9+peJh//pXhd9x1oSe1LBkZ3LEbCoygu8W0bXTukU1Jme2txKuotpCZRaC3FLxcQ== - cacache@^12.0.2: version "12.0.4" resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" @@ -1808,10 +1827,10 @@ caniuse-lite@^1.0.30001038: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001039.tgz#b3814a1c38ffeb23567f8323500c09526a577bbe" integrity sha512-SezbWCTT34eyFoWHgx8UWso7YtvtM7oosmFoXbCkdC6qJzRfBTeTgE9REtKtiuKXuMwWTZEvdnFNGAyVMorv8Q== -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +centra@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/centra/-/centra-2.4.2.tgz#3bad51dbca2250dbecad84598dc7184973f4c4a9" + integrity sha512-f1RaP0V1HqVNEXfLfjNBthB2yy3KnSGnPCnOPCFLUk9e/Z4rNJ8nBaJNnghflnp88mi1IT8mfmW+HlMS1/H+bg== chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" @@ -2403,6 +2422,20 @@ del@^4.1.1: pify "^4.0.1" rimraf "^2.6.3" +del@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952" + integrity sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ== + dependencies: + globby "^11.0.1" + graceful-fs "^4.2.4" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.2" + p-map "^4.0.0" + rimraf "^3.0.2" + slash "^3.0.0" + depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -2440,6 +2473,13 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + dmg-builder@22.4.1: version "22.4.1" resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-22.4.1.tgz#ab80d3d6e4ed8a1d38beddbfe97c8f7a794dd932" @@ -3120,6 +3160,18 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== +fast-glob@^3.1.1: + version "3.2.5" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" + integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -3130,6 +3182,13 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fastq@^1.6.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" + integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== + dependencies: + reusify "^1.0.4" + faye-websocket@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" @@ -3186,6 +3245,13 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -3415,7 +3481,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0: +glob-parent@^5.0.0, glob-parent@^5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -3519,6 +3585,18 @@ globalthis@^1.0.1: dependencies: define-properties "^1.1.3" +globby@^11.0.1: + version "11.0.3" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" + integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" @@ -3552,6 +3630,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== +graceful-fs@^4.2.4: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" @@ -3847,6 +3930,11 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -4109,12 +4197,17 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + is-obj@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-path-cwd@^2.0.0: +is-path-cwd@^2.0.0, is-path-cwd@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== @@ -4138,6 +4231,11 @@ is-path-inside@^3.0.1: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" @@ -4162,11 +4260,6 @@ is-stream@^1.1.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= -is-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" - integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== - is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -4590,6 +4683,11 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -4614,6 +4712,14 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -5135,6 +5241,13 @@ p-map@^3.0.0: dependencies: aggregate-error "^3.0.0" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-retry@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" @@ -5281,6 +5394,11 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -5297,6 +5415,18 @@ pend@~1.2.0: resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= +phin@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/phin/-/phin-3.5.1.tgz#41ce07aa7eb23b1f7ded89fc00a464addddac972" + integrity sha512-jgFO28IaiWAl0xk+zmqVx7neKVokWKU8YTQC5QlB45SZnEE53LH2saqJIcyIV557VX3Gk+TdR4rwWTc3P83DSA== + dependencies: + centra "^2.4.2" + +picomatch@^2.0.5, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -5568,6 +5698,11 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -5864,6 +5999,11 @@ retry@^0.12.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -5898,6 +6038,13 @@ roarr@^2.15.2: semver-compare "^1.0.0" sprintf-js "^1.1.2" +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.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" @@ -6128,6 +6275,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" @@ -6684,6 +6836,13 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"