diff options
-rw-r--r-- | .SRCINFO | 12 | ||||
-rw-r--r-- | PKGBUILD | 37 | ||||
-rw-r--r-- | replugged.patch | 291 | ||||
-rwxr-xr-x | replugged.sh | 7 | ||||
-rw-r--r-- | webpack.patch | 1658 |
5 files changed, 35 insertions, 1970 deletions
@@ -1,17 +1,15 @@ pkgbase = replugged-electron-git pkgdesc = A fork of Powercord, the lightweight discord client mod focused on simplicity and performance. - pkgver = r1784.e984d8fd + pkgver = r1997.4fa35914 pkgrel = 1 url = https://github.com/replugged-org/replugged install = replugged.install arch = any license = MIT makedepends = git - makedepends = npm + makedepends = pnpm depends = electron19 depends = discord-canary-electron-bin - depends = curl - depends = jq provides = replugged conflicts = replugged source = git+https://github.com/replugged-org/replugged.git#branch=main @@ -19,12 +17,10 @@ pkgbase = replugged-electron-git source = replugged.desktop source = replugged.png source = replugged.patch - source = webpack.patch md5sums = SKIP - md5sums = fa10b7595d4a5cb4a7735a6e36fc9e61 + md5sums = a94b9b81f16f2743504d390c6c0f45a0 md5sums = 9698a7fbd4af735bee89e74fa0b03dfe md5sums = 4ddcb11a1ec0a8a9585a6f0b685286b4 - md5sums = 80b126e9868616ba6c4caaebb716a62f - md5sums = 410cd8ba30fb07064295c898c2e99be0 + md5sums = 7705ffd25cebcc73277a1a957c1cbe63 pkgname = replugged-electron-git @@ -1,14 +1,18 @@ # Maintainer: Łukasz Mariański <lmarianski at protonmail dot com> + +_electron='electron19' +_discord='discord-canary' + pkgname=replugged-electron-git _pkgname="${pkgname%-electron-*}" -pkgver=r1784.e984d8fd +pkgver=r1997.4fa35914 pkgrel=1 pkgdesc="A fork of Powercord, the lightweight discord client mod focused on simplicity and performance." arch=('any') url="https://github.com/${_pkgname}-org/${_pkgname}" license=('MIT') -depends=('electron19' 'discord-canary-electron-bin' 'curl' 'jq') -makedepends=('git' 'npm') +depends=("$_electron" 'discord-canary-electron-bin') +makedepends=('git' 'pnpm') provides=("$_pkgname") conflicts=("$_pkgname") install="$_pkgname.install" @@ -17,14 +21,12 @@ source=("git+https://github.com/${_pkgname}-org/${_pkgname}.git#branch=${_branch "$_pkgname.sh" "$_pkgname.desktop" "$_pkgname.png" - "$_pkgname.patch" - "webpack.patch") + "$_pkgname.patch") md5sums=('SKIP' - 'fa10b7595d4a5cb4a7735a6e36fc9e61' + 'a94b9b81f16f2743504d390c6c0f45a0' '9698a7fbd4af735bee89e74fa0b03dfe' '4ddcb11a1ec0a8a9585a6f0b685286b4' - '80b126e9868616ba6c4caaebb716a62f' - '410cd8ba30fb07064295c898c2e99be0') + '7705ffd25cebcc73277a1a957c1cbe63') pkgver() { cd "$srcdir/$_pkgname" @@ -37,17 +39,17 @@ prepare() { patch -p1 -i "$srcdir/$_pkgname.patch" - sed -i "s:@PKG_UPSTREAM@:$_pkgname-org/$_pkgname:;s:@PKG_BRANCH@:${_branch}:;s:@PKG_REVISION@:$(git rev-parse ${_branch}):" src/Powercord/coremods/updater/index.js + sed -i "s/@ELECTRON@/$_electron/" "$srcdir/$_pkgname.sh" + sed -i "s/@DISCORD@/$_discord/" "$srcdir/$_pkgname/src/main/index.ts" - # Bring back the "new" webpack backend, needed because of contextIsolation - # git revert -X ours -n 8dbf24d9ec3cf0ea6589707230e1d2cd5285e187 - patch -p1 -i "$srcdir/webpack.patch" + # sed -i "s:@PKG_UPSTREAM@:$_pkgname-org/$_pkgname:;s:@PKG_BRANCH@:${_branch}:;s:@PKG_REVISION@:$(git rev-parse ${_branch}):" src/Powercord/coremods/updater/index.js } build() { cd "$srcdir/$_pkgname" - npm install --cache "${srcdir}/npm-cache" --omit=dev + pnpm i --store-dir "${srcdir}/pnpm-store" + pnpm build } package() { @@ -58,13 +60,8 @@ package() { install -dm755 "$pkgdir/usr/share/$_pkgname" - cp -ar * "$pkgdir/usr/share/$_pkgname" - rm -rf "$pkgdir/usr/share/$_pkgname/"{test,LICENSE,README.md,release.sh,jsconfig.json,injectors} - - ln -s "/usr/share/$_pkgname/src/fake_node_modules/powercord" "$pkgdir/usr/share/$_pkgname/node_modules/" - ln -s "/usr/share/$_pkgname/src/fake_node_modules/keybindutils" "$pkgdir/usr/share/$_pkgname/node_modules/" - - echo "require('./src/patcher.js');" > "$pkgdir/usr/share/$_pkgname/index.js" + cp -ar dist/* "$pkgdir/usr/share/$_pkgname/" + echo "require('./main.js');" > "$pkgdir/usr/share/$_pkgname/index.js" # chmod -R u+rwX,go+rX,go-w "$pkgdir/usr/share/$_pkgname" diff --git a/replugged.patch b/replugged.patch index a242ab6879a6..7d82a99f323f 100644 --- a/replugged.patch +++ b/replugged.patch @@ -1,278 +1,13 @@ -diff --git a/src/Powercord/coremods/moduleManager/components/manage/Base.jsx b/src/Powercord/coremods/moduleManager/components/manage/Base.jsx -index 0b8a45d9..3e69cf77 100644 ---- a/src/Powercord/coremods/moduleManager/components/manage/Base.jsx -+++ b/src/Powercord/coremods/moduleManager/components/manage/Base.jsx -@@ -3,6 +3,7 @@ const { shell } = require('electron'); - const { React, getModule, contextMenu, i18n: { Messages } } = require('powercord/webpack'); - const { Button, Tooltip, ContextMenu, Divider, Icons: { Overflow } } = require('powercord/components'); - const { TextInput } = require('powercord/components/settings'); -+const XDG_DATA_HOME = process.env.XDG_DATA_HOME || join(process.env.HOME, '.local', 'share'); - - class Base extends React.Component { - constructor () { -@@ -91,7 +92,7 @@ class Base extends React.Component { - { - type: 'button', - name: Messages[`REPLUGGED_${this.state.key}_OPEN_FOLDER`], -- onClick: () => shell.openPath(join(__dirname, '..', '..', '..', '..', '..', '..', this.constructor.name.toLowerCase())) -+ onClick: () => shell.openPath(join(XDG_DATA_HOME, 'replugged', this.constructor.name.toLowerCase())) - }, - { - type: 'button', -diff --git a/src/Powercord/coremods/moduleManager/index.js b/src/Powercord/coremods/moduleManager/index.js -index 2c3a2a9d..14b0f77a 100644 ---- a/src/Powercord/coremods/moduleManager/index.js -+++ b/src/Powercord/coremods/moduleManager/index.js -@@ -6,7 +6,7 @@ const { React, getModule, i18n: { Messages } } = require('powercord/webpack'); - const { PopoutWindow } = require('powercord/components'); - const { inject, uninject } = require('powercord/injector'); - const { findInReactTree, forceUpdateElement } = require('powercord/util'); --const { SpecialChannels: { CSS_SNIPPETS, STORE_PLUGINS, STORE_THEMES }, WEBSITE } = require('powercord/constants'); -+const { SpecialChannels: { CSS_SNIPPETS, STORE_PLUGINS, STORE_THEMES }, WEBSITE, SETTINGS_FOLDER } = require('powercord/constants'); - const { join } = require('path'); - const commands = require('./commands'); - const deeplinks = require('./deeplinks'); -@@ -23,7 +23,7 @@ const { injectContextMenu } = require('powercord/util'); - const Menu = getModule([ 'MenuItem' ], false); - - let _quickCSS = ''; --const _quickCSSFile = join(__dirname, '..', '..', '..', '..', 'settings', 'quickcss', 'main.css'); -+const _quickCSSFile = join(SETTINGS_FOLDER, 'quickcss', 'main.css'); - let _quickCSSElement; - - async function _installerInjectCtxMenu () { -diff --git a/src/Powercord/coremods/moduleManager/util/cloneRepo.js b/src/Powercord/coremods/moduleManager/util/cloneRepo.js -index f015ea76..eb058858 100644 ---- a/src/Powercord/coremods/moduleManager/util/cloneRepo.js -+++ b/src/Powercord/coremods/moduleManager/util/cloneRepo.js -@@ -4,16 +4,18 @@ const fs = require('fs'); - const { REPO_URL_REGEX } = require('./misc'); - const { i18n: { Messages } } = require('powercord/webpack'); - -+const XDG_DATA_HOME = process.env.XDG_DATA_HOME || join(process.env.HOME, '.local', 'share'); -+ - module.exports = async function download (url, powercord, type) { - return new Promise((resolve) => { - // const dir = type === 'plugin' ? join(__dirname, '..', '..') : join(__dirname, '..', '..', 'themes'); - let dir; - switch (type) { - case 'plugin': -- dir = join(__dirname, '..', '..', '..', '..', '..', 'plugins'); -+ dir = join(XDG_DATA_HOME, 'replugged', 'plugins'); - break; - case 'theme': -- dir = join(__dirname, '..', '..', '..', '..', '..', 'themes'); -+ dir = join(XDG_DATA_HOME, 'replugged', 'themes'); - break; - } - -diff --git a/src/Powercord/coremods/updater/components/Settings.jsx b/src/Powercord/coremods/updater/components/Settings.jsx -index b915af1c..fbe64da4 100644 ---- a/src/Powercord/coremods/updater/components/Settings.jsx -+++ b/src/Powercord/coremods/updater/components/Settings.jsx -@@ -82,7 +82,7 @@ module.exports = class UpdaterSettings extends React.PureComponent { - <div className="about"> - <div> - <span>{Messages.REPLUGGED_UPDATES_UPSTREAM}</span> -- <span>{powercord.gitInfos.upstream.replace(REPO_URL, Messages.REPLUGGED_UPDATES_UPSTREAM_OFFICIAL)}</span> -+ <span>{powercord.gitInfos.upstream.replace(REPO_URL, Messages.REPLUGGED_UPDATES_UPSTREAM_OFFICIAL+' (AUR)')}</span> - </div> - <div> - <span>{Messages.REPLUGGED_UPDATES_REVISION}</span> -diff --git a/src/Powercord/coremods/updater/components/Update.jsx b/src/Powercord/coremods/updater/components/Update.jsx -index 7d8fbcaa..ab5be0e9 100644 ---- a/src/Powercord/coremods/updater/components/Update.jsx -+++ b/src/Powercord/coremods/updater/components/Update.jsx -@@ -28,6 +28,7 @@ module.exports = class Update extends React.PureComponent { - </div> - </div> - <div className='summary'> -+ {name === 'Powercord' ? <span>Note: Please download the update from the AUR!</span> : ''} - {commits.map(commit => <div key={commit.id}> - <a href={`https://github.com/${repo}/commit/${commit.id}`} target='_blank'> - <code>{commit.id.substring(0, 7)}</code> -diff --git a/src/Powercord/coremods/updater/index.js b/src/Powercord/coremods/updater/index.js -index 770e195a..e542774c 100644 ---- a/src/Powercord/coremods/updater/index.js -+++ b/src/Powercord/coremods/updater/index.js -@@ -44,9 +44,6 @@ class Updater { - const themes = [ ...powercord.styleManager.themes.values() ]; - - const entities = plugins.concat(themes).filter(e => !disabled.includes(e.updateIdentifier) && e.isUpdatable()); -- if (!disabled.includes(powercord.updateIdentifier)) { -- entities.push(powercord); -- } - - let done = 0; - const updates = []; -@@ -232,33 +229,10 @@ class Updater { - } - - async getGitInfos () { -- const branch = await PowercordNative.exec('git branch', this.cwd) -- .then(({ stdout }) => -- stdout -- .toString() -- .split('\n') -- .find(l => l.startsWith('*')) -- .slice(2) -- .trim() -- ); -- -- const revision = await PowercordNative.exec(`git rev-parse ${branch}`, this.cwd) -- .then(r => r.stdout.toString().trim()); -- -- let upstream = '???'; -- -- const remoteBranch = await powercord.getUpstreamBranch(); -- if (remoteBranch) { -- const remote = remoteBranch.split('/')[0]; -- upstream = await PowercordNative.exec(`git remote get-url ${remote}`, this.cwd) -- .then(r => r.stdout.toString().match(/github\.com[:/]([\w-_]+\/[\w-_]+)/)?.[1] || -- r.stdout.toString().trim().match(/(.*):(.*\/.*)/)[2]); -- } -- - return { -- upstream, -- branch, -- revision -+ upstream: "@PKG_UPSTREAM@", -+ branch: "@PKG_BRANCH@", -+ revision: "@PKG_REVISION@" - }; - } - -diff --git a/src/Powercord/index.js b/src/Powercord/index.js -index 019e304c..0de3d47e 100644 ---- a/src/Powercord/index.js -+++ b/src/Powercord/index.js -@@ -253,6 +253,32 @@ class Powercord extends Updatable { - } - return success; - } -+ -+ async _getUpdateCommits () { -+ return []; -+ } -+ async getBranch () {return this.gitInfos.branch;} -+ async getGitRepo () {return this.gitInfos.upstream;} -+ async _checkForUpdates () { -+ const abort = new AbortController(); -+ const timeout = setTimeout(() => { -+ abort.abort(); -+ throw new Error('Timed out.'); -+ }, 10000); -+ -+ try { -+ const latestCommitSha = await exec(`curl https://api.github.com/repos/${this.gitInfos.upstream}/commits/${this.gitInfos.branch} | jq -r .sha`, { -+ cwd: this.entityPath, -+ signal: abort.signal -+ }).then(({ stdout }) => stdout.toString()); -+ -+ clearTimeout(timeout); -+ return !latestCommitSha.includes(this.gitInfos.revision); -+ } catch (e) { -+ clearTimeout(timeout); -+ return false; -+ } -+ } - } - - module.exports = Powercord; -diff --git a/src/Powercord/managers/plugins.js b/src/Powercord/managers/plugins.js -index c45b34fe..f0cd4dea 100644 ---- a/src/Powercord/managers/plugins.js -+++ b/src/Powercord/managers/plugins.js -@@ -1,10 +1,12 @@ --const { resolve } = require('path'); -+const { resolve, join } = require('path'); - const { readdirSync } = require('fs'); - const { rmdirRf } = require('powercord/util'); - -+const XDG_DATA_HOME = process.env.XDG_DATA_HOME || join(process.env.HOME, '.local', 'share'); -+ - module.exports = class PluginManager { - constructor () { -- this.pluginDir = resolve(__dirname, '..', '..', '..', 'plugins'); -+ this.pluginDir = resolve(XDG_DATA_HOME, 'replugged', 'plugins'); - this.plugins = new Map(); - - this.manifestKeys = [ 'name', 'version', 'description', 'author', 'license' ]; -diff --git a/src/Powercord/managers/styles.js b/src/Powercord/managers/styles.js -index c5b184e4..118b5025 100644 ---- a/src/Powercord/managers/styles.js -+++ b/src/Powercord/managers/styles.js -@@ -14,10 +14,12 @@ const ErrorTypes = Object.freeze({ - INVALID_MANIFEST: 'INVALID_MANIFEST' - }); - -+const XDG_DATA_HOME = process.env.XDG_DATA_HOME || join(process.env.HOME, '.local', 'share'); -+ - module.exports = class StyleManager { - constructor () { - this._coreStyles = []; -- this.themesDir = join(__dirname, '..', '..', '..', 'themes'); -+ this.themesDir = join(XDG_DATA_HOME, 'replugged', 'themes'); - this.themes = new Map(); - - if (!window.__SPLASH__) { -diff --git a/src/browserWindow.js b/src/browserWindow.js -index 82620e56..cafc41a0 100644 ---- a/src/browserWindow.js -+++ b/src/browserWindow.js -@@ -28,7 +28,6 @@ class PatchedBrowserWindow extends BrowserWindow { - if (opts.webPreferences.nativeWindowOpen) { - // Discord Client - opts.webPreferences.preload = join(__dirname, './preload.js'); -- opts.webPreferences.contextIsolation = false; // shrug - } else { - // Splash Screen on macOS (Host 0.0.262+) & Windows (Host 0.0.293 / 1.0.17+) - opts.webPreferences.preload = join(__dirname, './preloadSplash.js'); -diff --git a/src/fake_node_modules/powercord/constants.js b/src/fake_node_modules/powercord/constants.js -index 147e02e9..948f2b02 100644 ---- a/src/fake_node_modules/powercord/constants.js -+++ b/src/fake_node_modules/powercord/constants.js -@@ -1,5 +1,9 @@ - const { join } = require('path'); - -+const XDG_CONFIG_HOME = process.env.XDG_CONFIG_HOME || join(process.env.HOME, '.config'); -+const XDG_CACHE_HOME = process.env.XDG_CACHE_HOME || join(process.env.HOME, '.cache'); -+const XDG_DATA_HOME = process.env.XDG_DATA_HOME || join(process.env.HOME, '.local', 'share'); -+ - module.exports = Object.freeze({ - // Replugged - WEBSITE: 'https://replugged.dev', -@@ -7,9 +11,9 @@ module.exports = Object.freeze({ - REPO_URL: 'replugged-org/replugged', - - // Runtime -- SETTINGS_FOLDER: join(__dirname, '..', '..', '..', 'settings'), -- CACHE_FOLDER: join(__dirname, '..', '..', '..', '.cache'), -- LOGS_FOLDER: join(__dirname, '..', '..', '..', '.logs'), -+ SETTINGS_FOLDER: join(XDG_CONFIG_HOME, 'replugged'), -+ CACHE_FOLDER: join(XDG_CACHE_HOME, 'replugged'), -+ LOGS_FOLDER: join(XDG_DATA_HOME, 'replugged', 'logs'), - - // Discord Server - DISCORD_INVITE: 'B2TcnXV9Rg', -diff --git a/src/patcher.js b/src/patcher.js -index 372e41f5..eaace1cf 100644 ---- a/src/patcher.js -+++ b/src/patcher.js -@@ -7,7 +7,7 @@ const { existsSync, unlinkSync } = require('fs'); - - // Restore the classic path; The updater relies on it and it makes Discord go corrupt - const electronPath = require.resolve('electron'); --const discordPath = join(dirname(require.main.filename), '..', 'app.asar'); -+const discordPath = join('/', 'usr', 'lib', 'discord-canary', 'app.asar'); - require.main.filename = join(discordPath, 'app_bootstrap/index.js'); - - const electron = require('electron'); -@@ -30,7 +30,6 @@ function setAppUserModelId (...args) { - appSetAppUserModelId.apply(this, args); - if (!_patched) { - _patched = true; -- require('./updater.win32'); - } - } - +diff --git a/src/main/index.ts b/src/main/index.ts +index f8f225ec..6a18ed4c 100644 +--- a/src/main/index.ts ++++ b/src/main/index.ts +@@ -5,7 +5,7 @@ import type { RepluggedWebContents } from "../types"; + import { CONFIG_PATHS } from "src/util"; + + const electronPath = require.resolve("electron"); +-const discordPath = join(dirname(require.main!.filename), "..", "app.orig.asar"); ++const discordPath = join("/", "usr", "lib", "@DISCORD@", "app.asar"); + // require.main!.filename = discordMain; + + Object.defineProperty(global, "appSettings", { diff --git a/replugged.sh b/replugged.sh index d7f925555141..774cc2b4d2ba 100755 --- a/replugged.sh +++ b/replugged.sh @@ -1,8 +1,3 @@ #!/bin/sh -XDG_DATA_HOME="${XDG_DATA_HOME:=$HOME/.local/share}" -mkdir -p "$XDG_DATA_HOME/replugged/"{plugins,themes} - -ln -s "/usr/share/replugged/src/fake_node_modules" "$XDG_DATA_HOME/replugged/node_modules" &>/dev/null - -exec electron19 /usr/share/replugged "$@"
\ No newline at end of file +exec @ELECTRON@ /usr/share/replugged "$@"
\ No newline at end of file diff --git a/webpack.patch b/webpack.patch deleted file mode 100644 index 6c048dfa0a3d..000000000000 --- a/webpack.patch +++ /dev/null @@ -1,1658 +0,0 @@ -diff --git a/src/fake_node_modules/powercord/webpack/index.js b/src/fake_node_modules/powercord/webpack/index.js -index efe6bfe6..7a2e0e62 100644 ---- a/src/fake_node_modules/powercord/webpack/index.js -+++ b/src/fake_node_modules/powercord/webpack/index.js -@@ -1,210 +1,212 @@ --const { sleep } = require('powercord/util'); --const moduleFilters = require('./modules.json'); -- --/** --* @typedef WebpackInstance --* @property {object} cache --* @property {function} require --* @property {function} loadChunk --*/ -- --/** --* @typedef ContextMenuModule --* @property {function} openContextMenu --* @property {function} closeContextMenu --*/ -- -- --/** -- * @typedef ModuleInfo -- * @property {number} id The module's id. -- * @property {boolean} loaded Whether the module is loaded -- * @property {object} exports The module's exports -- */ -- --/** --* @typedef {number[]} chunkIds The chunk's ids. --* @typedef {object.<number, function>} chunkMdls A map of module ids to module source functions. --* @typedef {[chunkIds, chunkMdls]} Chunk --*/ -- --/** --* @property {ContextMenuModule} contextMenu --* @property {WebpackInstance} instance --*/ --const webpack = { -- ...require('./lazy'), -- -- /** -- * Grabs a module from the Webpack store -- * @param {function|string[]} filter Filter used to grab the module. Can be a function or an array of keys the object must have. -- * @param {boolean} retry Whether or not to retry fetching if the module is not found. Each try will be delayed by 100ms and max retries is 20. -- * @param {boolean} forever If Replugged should try to fetch the module forever. Should be used only if you're in early stages of startup. -- * @returns {Promise<object>|object} The found module. A promise will always be returned, unless retry is false. -- */ -- getModule (filter, retry = true, forever = false) { -- if (Array.isArray(filter)) { -- const keys = filter; -- filter = m => keys.every(key => m.hasOwnProperty(key) || (m.__proto__ && m.__proto__.hasOwnProperty(key))); -- } -- -- if (!retry) { -- return webpack._getModules(filter); -- } -- -- return new Promise(async (res) => { -- let mdl; -- for (let i = 0; i < 21; !forever && i++) { -- mdl = webpack._getModules(filter); -- if (mdl) { -- return res(mdl); -- } -- await sleep(100); -- } -- -- res(mdl); -- }); -- }, -- -- /** -- * Grabs all found modules from the webpack store -- * @param {function|string[]} filter Filter used to grab the module. Can be a function or an array of keys the object must have. -- * @returns {object[]} The found modules. -- */ -- getAllModules (filter) { -- if (Array.isArray(filter)) { -- const keys = filter; -- filter = m => keys.every(key => m.hasOwnProperty(key) || (m.__proto__ && m.__proto__.hasOwnProperty(key))); -- } -- -- return webpack._getModules(filter, true); -- }, -- -- /** -- * Grabs a React component by its display name -- * @param {string} displayName Component's display name. -- * @param {boolean} retry Whether or not to retry fetching if the module is not found. Each try will be delayed by 100ms and max retries is 20. -- * @param {boolean} forever If Replugged should try to fetch the module forever. Should be used only if you're in early stages of startup. -- * @returns {Promise<object>|object} The component. A promise will always be returned, unless retry is false. -- */ -- getModuleByDisplayName (displayName, retry = true, forever = false) { -- return webpack.getModule(m => m.displayName && m.displayName.toLowerCase() === displayName.toLowerCase(), retry, forever); -- }, -- -+if (!global.NEW_BACKEND) { -+ module.exports = require('./old.webpack.js'); -+ return; -+ } -+ -+ const { join } = require('path'); -+ const { readFile } = require('fs').promises; -+ const { webFrame, contextBridge } = require('electron'); -+ const { deserialize, freePointer, setCommandHandler } = require('./serialize.js'); -+ const moduleFilters = require('./modules.json'); -+ const MODULE_ID = Symbol.for('powercord.webpack.moduleId'); -+ - /** -- * Grabs a React component's module info by its display name -- * @param {string} displayName Component's display name. -- * @returns {ModuleInfo} The module info. -- */ -- getModuleInfoByDisplayName (displayName) { -- return Object.values(webpack.instance.cache).find(m => m?.exports?.displayName === displayName || m?.exports?.default?.displayName === displayName); -- }, -- -+ * @typedef WebpackInstance -+ * @property {object} cache -+ * @property {function} require -+ */ -+ - /** -- * Grabs a chunk by one of its modules' id -- * @param {number} id The module id. -- * @returns {Chunk} The chunk. -- */ -- getChunkByModuleId (id) { -- return webpackChunkdiscord_app.find((c) => id in c[1]); -- }, -- -+ * @typedef ContextMenuModule -+ * @property {function} openContextMenu -+ * @property {function} closeContextMenu -+ */ -+ -+ // -- -+ // Webpack interface -+ // -- -+ let commandHandler = null; -+ const DISPLAY_NAME_FN = /=>\s*(?:\w+\??.(\w+)(?:\?\.|\s*&&\s*\w+\.\1\.)displayName|_optionalChain\(\[\w+, 'optionalAccess', _2 => _2\.(\w+), 'optionalAccess', _3 => _3\.displayName]\))\s*={2,3}\s*['"](.*)['"]/; -+ -+ function processResult (res, all) { -+ if (!res) { -+ return res; -+ } -+ -+ if (res instanceof Promise) { -+ return res.then((r) => processResult(r, all)); -+ } -+ -+ if (all) { -+ return res.map((r) => processResult(r)); -+ } -+ -+ const mdl = deserialize(res[1]); -+ if (mdl && (typeof mdl === 'function' || typeof mdl === 'object')) { -+ // eslint-disable-next-line prefer-destructuring -+ mdl[MODULE_ID] = res[0]; -+ } -+ -+ return mdl; -+ } -+ - /** -- * Gets the source function of a module by its id -- * @param {number} id The module id. -- * @returns {function} The source function. -- */ -- getModuleSourceById (id) { -- const chunk = webpack.getChunkByModuleId(id); -- return chunk?.[1][id] ?? null; -- }, -- -+ * Grabs a module from the Webpack store -+ * @param {function|string[]} filter Filter used to grab the module. Can be a function or an array of keys the object must have. -+ * @param {boolean} retry Whether or not to retry fetching if the module is not found. Each try will be delayed by 100ms and max retries is 20. -+ * @param {boolean} forever If Replugged should try to fetch the module forever. Should be used only if you're in early stages of startup. -+ * @returns {Promise<object>|object} The found module. A promise will always be returned, unless retry is false. -+ */ -+ function getModule (filter, retry = true, forever = false) { -+ if (typeof filter === 'function') { -+ const match = filter.toString().match(DISPLAY_NAME_FN); -+ if (match) { -+ const res = commandHandler('getModuleByDisplayNameRaw', match[1] || match[2], match[3], retry, forever); -+ return processResult(res); -+ } -+ -+ const _filter = filter; -+ filter = (e) => !!_filter(deserialize(e)); -+ } -+ -+ const res = commandHandler('getModule', filter, retry, forever); -+ return processResult(res); -+ } -+ - /** -- * From a given module id, gets a list of chunk ids that said module might lazy load. -- * @param {number} id The module id. -- * @returns {number[]} The 'to be lazy-loaded' chunk ids -- */ -- getLazyLoadedChunkIdsByModuleId (id) { -- const srcStr = webpack.getModuleSourceById(id)?.toString(); -- if (!srcStr) { -- return []; -- } -- const requireArgument = srcStr.match(/^\(.,.,(.)\)/)?.[1]; -- -- if (!requireArgument) { -- return []; -- } -- -- const importPattern = new RegExp(`\\b${requireArgument}\\.e\\(\\d+\\)`, 'g'); -- const imports = srcStr.match(importPattern); -- const ids = imports.map(e => e.slice(4, -1)); -- return ids; -- }, -- -- -+ * Grabs all found modules from the webpack store -+ * @param {function|string[]} filter Filter used to grab the module. Can be a function or an array of keys the object must have. -+ * @returns {object[]} The found modules. -+ */ -+ function getAllModules (filter) { -+ if (typeof filter === 'function') { -+ const _filter = filter; -+ filter = (e) => !!_filter(deserialize(e)); -+ } -+ -+ const res = commandHandler('getAllModules', filter); -+ return processResult(res, true); -+ } -+ - /** -- * Initializes the injection into Webpack -- * @returns {Promise<void>} -- */ -- async init () { -- delete webpack.init; -- -- // Wait until webpack is ready -- while (!window.webpackChunkdiscord_app || !window._) { -- await sleep(100); -- } -- -- // Extract values from webpack -- webpack.instance = {}; -- webpackChunkdiscord_app.push([ -- [ [ '_powercord' ] ], -- {}, -- (r) => { -- webpack.instance.cache = r.c; -- webpack.instance.require = (m) => r(m); -- -- webpack.instance.loadChunk = (c) => r.e(c) -- .then(() => { -- // Get chunk -- const chunk = webpackChunkdiscord_app.find(C => `${C[0][0]}` === c); -- // Cache all the modules -- Object.keys(chunk[1]).forEach(m => r(m)); -- }); -- } -- ]); -- webpackChunkdiscord_app.pop(); -- -- // Patch push to enable webpack chunk listeners -- webpack._patchPush(); -- delete webpack._patchPush; -- -- // Load modules pre-fetched -- for (const mdl in moduleFilters) { -- // noinspection JSUnfilteredForInLoop -- this[mdl] = await webpack.getModule(moduleFilters[mdl]); -- } -- -- this.i18n = webpack.getAllModules([ 'Messages', 'getLanguages' ]).find((m) => m.Messages.ACCOUNT); -- }, -- -- _getModules (filter, all = false) { -- const moduleInstances = Object.values(webpack.instance.cache).filter(m => m.exports); -- if (all) { -- const exports = moduleInstances.filter(m => filter(m.exports)).map(m => m.exports); -- const expDefault = moduleInstances.filter(m => m.exports.default && filter(m.exports.default)).map(m => m.exports.default); -- return exports.concat(expDefault); -- } -- -- const exports = moduleInstances.find(m => filter(m.exports)); -- if (exports) { -- return exports.exports; -- } -- const expDefault = moduleInstances.find(m => m.exports.default && filter(m.exports.default)); -- if (expDefault) { -- return expDefault.exports.default; -- } -- return null; -+ * Grabs a React component by its display name -+ * @param {string} displayName Component's display name. -+ * @param {boolean} retry Whether or not to retry fetching if the module is not found. Each try will be delayed by 100ms and max retries is 20. -+ * @param {boolean} forever If Replugged should try to fetch the module forever. Should be used only if you're in early stages of startup. -+ * @returns {Promise<object>|object} The component. A promise will always be returned, unless retry is false. -+ */ -+ function getModuleByDisplayName (displayName, retry = true, forever = false) { -+ const res = commandHandler('getModuleByDisplayName', displayName.toLowerCase(), retry, forever); -+ return processResult(res); -+ } -+ -+ function getModuleById (id) { -+ const res = commandHandler('getModuleById', id); -+ return processResult(res); - } --}; -- --module.exports = webpack; -+ -+ let elementPointer = 0; -+ function lookupReactReference (element) { -+ if (!element) { -+ return; -+ } -+ -+ if (!('__reactFiber$' in element)) { -+ Object.defineProperty(element, '__reactFiber$', { -+ get: () => { -+ element.dataset.powercordPointer = elementPointer; -+ const res = commandHandler('lookupReactReference', elementPointer++); -+ element.removeAttribute('data-powercord-pointer'); -+ return deserialize(res); -+ } -+ }); -+ } -+ } -+ -+ /** -+ * @property {ContextMenuModule} contextMenu -+ * @property {WebpackInstance} instance -+ */ -+ const webpack = { -+ getModule, -+ getAllModules, -+ getModuleByDisplayName, -+ require: getModuleById, -+ -+ // Internal tape stuff -+ __lookupReactReference: lookupReactReference, -+ -+ /** -+ * Initializes the injection into Webpack -+ * @returns {Promise<void>} -+ */ -+ async init () { -+ delete webpack.init; -+ -+ // Init proxy script -+ const serializeScript = await readFile(join(__dirname, 'serialize.js'), 'utf8'); -+ const proxyScript = await readFile(join(__dirname, 'proxy.js'), 'utf8'); -+ await webFrame.executeJavaScript(`(function () { ${serializeScript} ${proxyScript} return init() }())`); -+ -+ // Load modules pre-fetched -+ for (const mdl in moduleFilters) { -+ // noinspection JSUnfilteredForInLoop -+ this[mdl] = await getModule(moduleFilters[mdl]); -+ } -+ -+ this.i18n = getAllModules([ 'Messages', 'getLanguages' ]).find((m) => m.Messages.ACCOUNT); -+ -+ // Expose window stuff -+ this.proxiedWindow = await commandHandler('getWindowProps'); -+ this.proxiedWindow.DiscordSentry = deserialize(this.proxiedWindow.DiscordSentry); -+ this.proxiedWindow.__SENTRY__ = deserialize(this.proxiedWindow.__SENTRY__); -+ this.proxiedWindow._ = deserialize(this.proxiedWindow._); -+ } -+ }; -+ -+ contextBridge.exposeInMainWorld('__$$WebpackProxyIPC', { -+ freePointer: (ptr) => freePointer(ptr), -+ registerCommandHandler: (h) => { -+ if (commandHandler) { -+ throw new Error('no'); -+ } -+ -+ commandHandler = h; -+ setCommandHandler(h); -+ } -+ }); -+ -+ console.inspect = function (obj) { -+ if ('__$$pointer' in obj) { -+ commandHandler('inspect', obj.__$$pointer); -+ return; -+ } -+ -+ console.log(obj); -+ }; -+ -+ const remoteUrls = new Set(); -+ const cou = URL.createObjectURL; -+ const rou = URL.revokeObjectURL; -+ URL.createObjectURL = function (blob) { -+ if ('__$$pointer' in blob) { -+ const url = commandHandler('createUrlObject', blob.__$$pointer); -+ remoteUrls.add(url); -+ return url; -+ } -+ -+ return cou.call(URL, blob); -+ }; -+ -+ URL.revokeObjectURL = function (url) { -+ if (remoteUrls.has(url)) { -+ remoteUrls.delete(url); -+ webFrame.executeJavaScript(`URL.revokeObjectURL(${JSON.stringify(url)})`); -+ return; -+ } -+ -+ rou.call(URL, url); -+ }; -+ -+ module.exports = webpack; -+ -\ No newline at end of file -diff --git a/src/fake_node_modules/powercord/webpack/proxy.js b/src/fake_node_modules/powercord/webpack/proxy.js -new file mode 100644 -index 00000000..50f3f972 ---- /dev/null -+++ b/src/fake_node_modules/powercord/webpack/proxy.js -@@ -0,0 +1,318 @@ -+/* global serialize, deserialize */ -+/* global readPointer, freePointer */ -+/* global __$$WebpackProxyIPC */ -+ -+const LOCAL = Symbol('powercord.webpack.local'); -+const KEY = Symbol('powercord.webpack.key'); -+ -+// -- -+// Initialization & globals -+// -- -+const webpackInstance = {}; -+const cache = {}; -+// eslint-disable-next-line no-unused-vars -+async function init () { -+ while (!window.webpackChunkdiscord_app || !window._) { -+ await new Promise((resolve) => setTimeout(resolve, 100)); -+ } -+ -+ // Extract values from webpack -+ webpackChunkdiscord_app.push([ -+ [ [ '_powercord' ] ], -+ {}, -+ (r) => { -+ webpackInstance.cache = r.c; -+ webpackInstance.require = (m) => r(m); -+ } -+ ]); -+} -+ -+ -+// -- -+// Module fetching (+ window globals) -+// -- -+function _getModules (filter, all) { -+ let key; -+ if (Array.isArray(filter)) { -+ const keys = filter; -+ filter = (m) => keys.every((k) => m.hasOwnProperty(k) || (m.__proto__ && m.__proto__.hasOwnProperty(k))); -+ key = `props:${keys.join(',')}`; -+ } else if (!filter[LOCAL]) { -+ const _filter = filter; -+ filter = (m) => _filter(serialize(m)); -+ } else { -+ key = filter[KEY]; -+ } -+ -+ if (!all && cache[key]) { -+ return cache[key]; -+ } -+ -+ const moduleInstances = Object.values(webpackInstance.cache).filter((m) => m.exports); -+ if (all) { -+ const exports = moduleInstances.filter((m) => filter(m.exports)).map((m) => [ m.i, m.exports ]); -+ const expDefault = moduleInstances.filter((m) => m.exports.default && filter(m.exports.default)).map((m) => [ m.i, m.exports.default ]); -+ return exports.concat(expDefault); -+ } -+ -+ const exports = moduleInstances.find((m) => filter(m.exports)); -+ if (exports) { -+ if (key) { -+ cache[key] = [ exports.i, exports.exports ]; -+ } -+ -+ return [ exports.i, exports.exports ]; -+ } -+ -+ const expDefault = moduleInstances.find((m) => m.exports.default && filter(m.exports.default)); -+ if (expDefault) { -+ if (key) { -+ cache[key] = [ expDefault.i, expDefault.exports.default ]; -+ } -+ -+ return [ expDefault.i, expDefault.exports.default ]; -+ } -+ -+ return null; -+} -+ -+function getModule (filter, retry, forever) { -+ if (!retry) { -+ return _getModules(filter); -+ } -+ -+ return new Promise(async (res) => { -+ let mdl; -+ for (let i = 0; i < (forever ? 666 : 21); i++) { -+ mdl = _getModules(filter); -+ if (mdl) { -+ return res(mdl); -+ } -+ -+ await new Promise((resolve) => setTimeout(resolve, 100)); -+ } -+ -+ res(null); -+ }); -+} -+ -+function getModuleByDisplayName (displayName, retry, forever) { -+ const filter = (m) => m.displayName?.toLowerCase() === displayName; -+ filter[KEY] = `name:${displayName}`; -+ filter[LOCAL] = true; -+ -+ return getModule(filter, retry, forever); -+} -+ -+function getModuleByDisplayNameRaw (prop, displayName, retry, forever) { -+ const filter = (m) => m[prop]?.displayName === displayName; -+ filter[KEY] = `name:${prop},${displayName}`; -+ filter[LOCAL] = true; -+ -+ return getModule(filter, retry, forever); -+} -+ -+function getModuleById (id) { -+ const mdl = webpackInstance.require(id); -+ if (mdl) { -+ return [ id, mdl ]; -+ } -+} -+ -+function getWindowProps () { -+ return { -+ platform: window.platform, -+ GLOBAL_ENV: window.GLOBAL_ENV, -+ DiscordSentry: serialize(window.DiscordSentry), -+ __SENTRY__: serialize(window.__SENTRY__), -+ _: serialize(window._) -+ }; -+} -+ -+function lookupReactReference (ptr) { -+ const element = document.querySelector(`[data-powercord-pointer="${ptr}"]`); -+ const key = Object.keys(element).find((k) => k.startsWith('__reactFiber') || k.startsWith('__reactInternalInstance')); -+ const fiber = element[key]; -+ return serialize(fiber); -+} -+ -+ -+// -- -+// Pointer operatons (lookup, ...) -+// -- -+function getObjectProperty (ptr, key) { -+ const obj = readPointer(ptr); -+ return serialize(obj[key]); -+} -+ -+function setObjectProperty (ptr, key, value) { -+ const obj = readPointer(ptr); -+ obj[key] = deserialize(value); -+} -+ -+function defineObjectProperty (ptr, key, desc) { -+ const obj = readPointer(ptr); -+ Object.defineProperty(obj, key, deserialize(desc)); -+} -+ -+function deleteObjectProperty (ptr, key) { -+ const obj = readPointer(ptr); -+ delete obj[key]; -+} -+ -+function getObjectOwnKeys (ptr) { -+ const obj = readPointer(ptr); -+ return Reflect.ownKeys(obj); -+} -+ -+function hasObjectKey (ptr, key) { -+ const obj = readPointer(ptr); -+ return Object.prototype.hasOwnProperty.call(obj, key); -+} -+ -+function getObjectPropertyDescriptor (ptr, key) { -+ const obj = readPointer(ptr); -+ let desc; -+ let target = obj; -+ while (!desc && target) { -+ desc = Object.getOwnPropertyDescriptor(target, key); -+ target = Object.getPrototypeOf(target); -+ } -+ -+ if (!desc) { -+ return void 0; -+ } -+ -+ const res = { -+ configurable: desc.configurable, -+ enumerable: desc.enumerable, -+ writable: desc.writable -+ }; -+ -+ if ('value' in desc) { -+ res.value = serialize(desc.value); -+ } -+ -+ if ('get' in desc) { -+ res.get = serialize(desc.get); -+ } -+ -+ if ('set' in desc) { -+ res.set = serialize(desc.set); -+ } -+ -+ return res; -+} -+ -+function invokeFunction (ptr, thisArg, args) { -+ const fn = readPointer(ptr); -+ const res = fn.call(deserialize(thisArg), ...deserialize(args)); -+ return serialize(res); -+} -+ -+function instantiateClass (ptr, args) { -+ const Klass = readPointer(ptr); -+ const instance = new Klass(...deserialize(args)); -+ return serialize(instance); -+} -+ -+function performArrayOperation (op, ptr, args) { -+ const array = readPointer(ptr); -+ if (op === 'set') { -+ array[args[0]] = deserialize(args[1]); -+ return; -+ } -+ -+ array[op](...deserialize(args)); -+} -+ -+ -+// -- -+// Entry point -+// -- -+function _serializeModuleRes (res, all) { -+ if (!res) { -+ return res; -+ } -+ -+ if (res instanceof Promise) { -+ return res.then((r) => _serializeModuleRes(r, all)); -+ } -+ -+ if (all) { -+ return res.map((m) => _serializeModuleRes(m)); -+ } -+ -+ return [ res[0], serialize(res[1]) ]; -+} -+ -+function commandHandler (cmd, ...args) { -+ switch (cmd) { -+ case 'getModule': -+ return _serializeModuleRes(getModule(args[0], args[1], args[2])); -+ case 'getModuleByDisplayName': -+ return _serializeModuleRes(getModuleByDisplayName(args[0], args[1], args[2])); -+ case 'getModuleByDisplayNameRaw': -+ return _serializeModuleRes(getModuleByDisplayNameRaw(args[0], args[1], args[2], args[3])); -+ case 'getModuleById': -+ return _serializeModuleRes(getModuleById(args[0])); -+ case 'getAllModules': -+ return _serializeModuleRes(_getModules(args[0], true), true); -+ case 'getWindowProps': -+ return getWindowProps(); -+ -+ case 'lookupReactReference': -+ return lookupReactReference(args[0]); -+ -+ case 'getObjectProperty': -+ return getObjectProperty(args[0], args[1]); -+ case 'setObjectProperty': -+ return setObjectProperty(args[0], args[1], args[2]); -+ case 'defineObjectProperty': -+ return defineObjectProperty(args[0], args[1], args[2]); -+ case 'deleteObjectProperty': -+ return deleteObjectProperty(args[0], args[1]); -+ case 'hasObjectKey': -+ return hasObjectKey(args[0], args[1]); -+ case 'getObjectOwnKeys': -+ return getObjectOwnKeys(args[0]); -+ case 'getObjectPropertyDescriptor': -+ return getObjectPropertyDescriptor(args[0], args[1]); -+ -+ case 'invokeFunction': -+ return invokeFunction(args[0], args[1], args[2]); -+ case 'instantiateClass': -+ return instantiateClass(args[0], args[1]); -+ case 'performArrayOperation': -+ return performArrayOperation(args[0], args[1], args[2]); -+ -+ case 'releasePointer': -+ return freePointer(args[0]); -+ case 'inspect': -+ return console.log(readPointer(args[0])); -+ -+ case 'createUrlObject': -+ return URL.createObjectURL(readPointer(args[0])); -+ } -+ -+ console.log(cmd, args); -+} -+ -+__$$WebpackProxyIPC.registerCommandHandler(commandHandler); -+// eslint-disable-next-line no-undef -+_commandHandler = commandHandler; -+ -+const mkfn = (fn) => (...args) => { -+ if (typeof args[0] === 'function') { -+ args[0][LOCAL] = true; -+ } -+ -+ return fn(...args)?.[1]; -+}; -+ -+window.$PowercordWebpack = { -+ getModule: mkfn(getModule), -+ getAllModules: mkfn((a) => _getModules(a, true)), -+ getModuleByDisplayName: mkfn((a, b, c) => getModuleByDisplayName(a.toLowerCase(), b, c)), -+ require: mkfn(getModuleById) -+}; -diff --git a/src/fake_node_modules/powercord/webpack/serialize.js b/src/fake_node_modules/powercord/webpack/serialize.js -new file mode 100644 -index 00000000..6f24e410 ---- /dev/null -+++ b/src/fake_node_modules/powercord/webpack/serialize.js -@@ -0,0 +1,906 @@ -+/* global FinalizationRegistry, WeakRef, __$$WebpackProxyIPC */ -+ -+const EXPOSE_DEBUGGING_HELPER = false; -+const PTR_MARKER = Symbol.for('powercord.serialize.pointer-marker'); -+const FUNCTION_ID = Symbol.for('powercord.serialize.function-id'); -+const MODULE_ID = Symbol.for('powercord.webpack.module-id'); -+const isMainWorld = typeof require === 'undefined'; -+let _commandHandler = () => void 0; -+ -+// -- -+// Memory management -+// MAIN WORLD SIDE -+// -- -+let ptr = 0; -+const memorySpace = new Map(); -+const pointerMap = new WeakMap(); -+const mainWorldPointers = new WeakMap(); -+ -+const pointerCache = new Map(); -+const functionCache = new Map(); -+ -+function allocatePointer (obj) { -+ if (!pointerMap.has(obj)) { -+ memorySpace.set(ptr, obj); -+ pointerMap.set(obj, ptr++); -+ } -+ -+ return pointerMap.get(obj); -+} -+ -+function freePointer (ptr) { -+ pointerMap.delete(memorySpace.get(ptr)); -+ memorySpace.delete(ptr); -+ pointerCache.delete(ptr); -+} -+ -+function readPointer (ptr) { -+ return memorySpace.get(ptr); -+} -+ -+ -+// -- -+// Memory management -+// RENDERER WORLD SIDE -+// -- -+const usedPointers = {}; -+const localPointerProperties = {}; -+const registry = new FinalizationRegistry((ptr) => { -+ usedPointers[ptr]--; -+ if (usedPointers[ptr] === 0) { -+ delete usedPointers[ptr]; -+ pointerCache.delete(ptr); -+ if (ptr in localPointerProperties) { -+ delete localPointerProperties[ptr]; -+ } -+ -+ if (isMainWorld) { -+ __$$WebpackProxyIPC.freePointer(ptr); -+ } else { -+ _commandHandler('releasePointer', ptr); -+ } -+ } -+}); -+ -+function usePointer (obj, ptr) { -+ usedPointers[ptr] = (usedPointers[ptr] || 0) + 1; -+ // if (usedPointers[ptr] > 1 && typeof ptr !== 'number') console.log('?????????'); -+ registry.register(obj, ptr); -+} -+ -+function cachePointer (ptr, val) { -+ pointerCache.set(ptr, new WeakRef(val)); -+} -+ -+function getCachedPointer (ptr) { -+ return pointerCache.get(ptr)?.deref(); -+} -+ -+function cacheFunction (ptr, val) { -+ functionCache.set(ptr, new WeakRef(val)); -+} -+ -+function getCachedFunction (ptr) { -+ return functionCache.get(ptr)?.deref(); -+} -+ -+ -+// -- -+// Real job -+// -- -+function isCloneable (obj) { -+ if (obj === null || obj === void 0) { -+ return true; -+ } -+ -+ // Ref: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm -+ const type = typeof obj; -+ if (type === 'object') { -+ return obj instanceof Date || -+ obj instanceof RegExp || -+ obj instanceof Blob || -+ obj instanceof File || -+ obj instanceof FileList || -+ obj instanceof ArrayBuffer || -+ obj instanceof ImageBitmap || -+ obj instanceof ImageData; -+ } -+ -+ return type === 'boolean' || -+ type === 'string' || -+ type === 'number' || -+ type === 'bigint'; -+} -+ -+let fnRefId = 0; -+let classRefId = 0; -+const classMap = new WeakMap(); -+let htmlSerialId = isMainWorld ? 0 : 1; -+ -+function _serializeObjectData (obj, seen) { -+ const res = {}; -+ let target = obj; -+ while (target && target !== Object.prototype) { -+ for (const key of Reflect.ownKeys(target)) { -+ if (key in res || (typeof key === 'string' && key.startsWith('__$$'))) { -+ continue; -+ } -+ -+ const desc = Reflect.getOwnPropertyDescriptor(target, key); -+ if ('value' in desc) { -+ // eslint-disable-next-line no-use-before-define -+ res[key] = serialize(target[key], seen, false); -+ } else if ('get' in desc) { -+ const fakeDesc = { -+ configurable: desc.configurable, -+ enumerable: desc.enumerable, -+ // eslint-disable-next-line no-use-before-define -+ get: () => serialize(target[key]) -+ }; -+ -+ res[key] = { -+ $$type: 'desc', -+ desc: fakeDesc -+ }; -+ } -+ } -+ -+ target = Reflect.getPrototypeOf(target); -+ } -+ -+ return res; -+} -+ -+function serialize (obj, seen = new WeakMap()) { -+ if (isCloneable(obj)) { -+ return obj; -+ } -+ -+ if (typeof obj === 'symbol') { -+ return { -+ $$type: '$symbol', -+ value: Symbol.keyFor(obj) -+ }; -+ } -+ -+ if (obj instanceof Promise) { -+ return obj.then((v) => serialize(v)); -+ } -+ -+ if (obj === window) { -+ return { $$type: '$window' }; -+ } -+ -+ if (obj === document) { -+ return { $$type: '$document' }; -+ } -+ -+ if (obj instanceof Element) { -+ obj.dataset.powercordSerializeId = htmlSerialId; -+ const ptr = htmlSerialId; -+ htmlSerialId += 2; -+ return { -+ $$type: '$html', -+ ptr -+ }; -+ } -+ -+ if (mainWorldPointers.has(obj)) { -+ return { -+ $$type: '$pointerRender', -+ ptr: mainWorldPointers.get(obj) -+ }; -+ } -+ -+ if (isMainWorld && !obj.__$$pure) { -+ const ptr = allocatePointer(obj); -+ if (Array.isArray(obj)) { -+ return { -+ $$type: '$array', -+ data: obj.map((v) => serialize(v, seen)), -+ ptr -+ }; -+ } -+ -+ const type = typeof obj === 'function' -+ ? obj.prototype && Object.keys(obj.prototype).length ? 'class' : 'function' -+ : 'object'; -+ -+ return { -+ $$type: '$pointer', -+ ptr, -+ type -+ }; -+ } -+ -+ if ('__$$pointer' in obj) { -+ if (obj.__$$pointerType === 'class' && !obj.prototype.constructor.__$$props) { -+ const serializedProto = {}; -+ for (const mth of Reflect.ownKeys(obj.prototype)) { -+ if ([ 'constructor', '__$$remotePrototype' ].includes(mth)) { -+ continue; -+ } -+ -+ serializedProto[mth] = serialize(obj.prototype[mth]); -+ } -+ -+ function construct (args, ptr) { -+ obj.prototype.__$$ptr = ptr; -+ obj.prototype.__$$marker = PTR_MARKER; -+ // eslint-disable-next-line new-cap, no-use-before-define -+ const instance = new obj(...deserialize(args)); -+ usePointer(instance, ptr); -+ return serialize(instance); -+ } -+ -+ const ref = classMap.get(obj) ?? classRefId++; -+ classMap.set(obj, ref); -+ return { -+ $$type: '$pointer', -+ type: 'class', -+ ptr: obj.__$$pointer, -+ props: serializedProto, -+ construct, -+ ref -+ }; -+ } -+ -+ return { -+ $$type: '$pointer', -+ ptr: obj.__$$pointer -+ }; -+ } -+ -+ if (seen.has(obj)) { -+ return seen.get(obj); -+ } -+ -+ if (Array.isArray(obj)) { -+ const res = []; -+ seen.set(obj, res); -+ for (const v of obj) { -+ res.push(serialize(v, seen)); -+ } -+ -+ return res; -+ } -+ -+ if (typeof obj === 'function') { -+ const wrapper = (...args) => { -+ // eslint-disable-next-line no-use-before-define -+ const preparedArgs = deserialize(args); -+ const res = obj.call(...preparedArgs); -+ return serialize(res); -+ }; -+ -+ if (!(FUNCTION_ID in obj)) { -+ obj[FUNCTION_ID] = fnRefId++; -+ } -+ -+ const res = { -+ $$type: '$function', -+ fn: wrapper, -+ ref: obj[FUNCTION_ID], -+ str: obj.toString() -+ }; -+ -+ seen.set(obj, res); -+ return res; -+ } -+ -+ if (obj instanceof Set) { -+ const res = new Set(); -+ seen.set(obj, res); -+ for (const item of obj) { -+ res.add(serialize(item, seen)); -+ } -+ -+ return res; -+ } -+ -+ if (obj instanceof Map) { -+ const res = new Map(); -+ seen.set(obj, res); -+ for (const k in obj) { -+ if (k in obj) { -+ res.set(k, serialize(res[k], seen)); -+ } -+ } -+ -+ return res; -+ } -+ -+ const res = {}; -+ seen.set(obj, res); -+ const objProto = Reflect.getPrototypeOf(obj); -+ if (!objProto || objProto === Reflect.getPrototypeOf({})) { -+ for (const k in obj) { -+ if (k in obj) { -+ res[k] = serialize(obj[k], seen); -+ } -+ } -+ -+ return res; -+ } -+ -+ if (!isMainWorld) { -+ res.__$$pointerRender = allocatePointer(obj); -+ res.getData = () => _serializeObjectData(obj); -+ } else { -+ Object.assign(res, _serializeObjectData(obj)); -+ } -+ -+ return res; -+} -+ -+const pointerProxyHandler = { -+ get: (target, key) => { -+ const props = target.__$$props || target; -+ if (key === '__$$pointer') { -+ return props.ptr; -+ } -+ -+ if (key === '__$$pointerType') { -+ return props.type; -+ } -+ -+ if (key === 'prototype') { -+ if (target.prototype) { -+ const remote = _commandHandler('getObjectProperty', props.ptr, 'prototype'); -+ // eslint-disable-next-line no-use-before-define -+ target.prototype.__$$remotePrototype = deserialize(remote); -+ } -+ -+ return target.prototype; -+ } -+ -+ if (key === 'remotePrototype') { -+ key = 'prototype'; -+ } -+ -+ if (typeof target === 'function' && [ 'bind', 'call', 'apply' ].includes(key)) { -+ return target[key]; -+ } -+ -+ if (props.ptr in localPointerProperties && key in localPointerProperties[props.ptr]) { -+ return localPointerProperties[props.ptr][key]; -+ } -+ -+ const res = _commandHandler('getObjectProperty', props.ptr, key); -+ // eslint-disable-next-line no-use-before-define -+ return deserialize(res); -+ }, -+ set: (target, key, value) => { -+ const props = target.__$$props || target; -+ if (!localPointerProperties[props.ptr]) { -+ localPointerProperties[props.ptr] = Object.create(null); -+ } -+ -+ if (key === MODULE_ID || (value && typeof value === 'object' && !('__$$pointer' in value))) { -+ // Cache local objects to avoid unnecessary IPC roundtrips. -+ if (key === MODULE_ID) { -+ localPointerProperties[props.ptr][key] = value; -+ } -+ } -+ -+ if (key === MODULE_ID) { -+ return true; -+ } -+ -+ _commandHandler('setObjectProperty', props.ptr, key, serialize(value)); -+ return true; -+ }, -+ defineProperty: (target, key, descriptor) => { -+ const props = target.__$$props || target; -+ _commandHandler('defineObjectProperty', props.ptr, key, serialize(descriptor)); -+ return true; -+ }, -+ deleteProperty: (target, key) => { -+ const props = target.__$$props || target; -+ _commandHandler('deleteObjectProperty', props.ptr, key); -+ return true; -+ }, -+ has: (target, key) => { -+ if ([ '__$$pointer', '__$$pointerType' ].includes(key)) { -+ return true; -+ } -+ -+ const props = target.__$$props || target; -+ return _commandHandler('hasObjectKey', props.ptr, key); -+ }, -+ ownKeys: (target) => { -+ const props = target.__$$props || target; -+ const targetKeys = '$$type' in target ? [] : Reflect.ownKeys(target); -+ const remoteKeys = _commandHandler('getObjectOwnKeys', props.ptr); -+ for (const key of remoteKeys) { -+ if (targetKeys.indexOf(key) === -1) { -+ targetKeys.push(key); -+ } -+ } -+ -+ return targetKeys; -+ }, -+ getOwnPropertyDescriptor: (target, key) => { -+ const props = target.__$$props || target; -+ const targetDesc = Object.getOwnPropertyDescriptor(target, key); -+ if (targetDesc && !targetDesc.configurable) { -+ return targetDesc; -+ } -+ -+ if (!props.$$cachedDescriptors) { -+ props.$$cachedDescriptors = Object.create(null); -+ } -+ -+ if (key in props.$$cachedDescriptors) { -+ return props.$$cachedDescriptors[key]; -+ } -+ -+ // eslint-disable-next-line no-use-before-define -+ const desc = _commandHandler('getObjectPropertyDescriptor', props.ptr, key); -+ if (!desc) { -+ return void 0; -+ } -+ -+ const res = { -+ configurable: true, -+ enumerable: desc.enumerable -+ }; -+ -+ if ('value' in desc) { -+ res.writable = desc.writable; -+ // eslint-disable-next-line no-use-before-define -+ res.value = deserialize(desc.value); -+ } -+ -+ if ('get' in desc) { -+ // eslint-disable-next-line no-use-before-define -+ res.get = deserialize(desc.get); -+ } -+ -+ if ('set' in desc) { -+ // eslint-disable-next-line no-use-before-define -+ res.set = deserialize(desc.set); -+ } -+ -+ props.$$cachedDescriptors[key] = res; -+ return res; -+ }, -+ getPrototypeOf: (target) => { -+ const props = target.__$$props || target; -+ if (props.__$$proto) { -+ return props.__$$proto; -+ } -+ -+ const res = _commandHandler('getObjectProperty', props.ptr, '__proto__'); -+ // eslint-disable-next-line no-use-before-define -+ const dres = deserialize(res); -+ props.__$$proto = dres; -+ return dres; -+ }, -+ apply: (target, thisArg, args) => { -+ const props = target.__$$props || target; -+ if (props.type === 'class') { -+ // eslint-disable-next-line new-cap -+ return new target(...args); -+ } -+ -+ return target.call(thisArg, ...args); -+ } -+}; -+ -+const classProxyHandler = { -+ get: (target, key) => { -+ if (key in target) { -+ return target[key]; -+ } -+ -+ if (key === '__$$pointer') { -+ return target.__$$remotePtr; -+ } -+ -+ if (key === '__$$pointerType') { -+ return 'object'; -+ } -+ -+ const res = _commandHandler('getObjectProperty', target.__$$remotePtr, key); -+ // eslint-disable-next-line no-use-before-define -+ return deserialize(res); -+ }, -+ set: (target, key, value) => { -+ if (key in target || key.startsWith('__$$')) { -+ target[key] = value; -+ } -+ -+ if (![ '__$$remotePtr', MODULE_ID ].includes(key)) { -+ _commandHandler('setObjectProperty', target.__$$remotePtr, key, serialize(value)); -+ } -+ -+ return true; -+ }, -+ deleteProperty: (target, key) => { -+ _commandHandler('deleteObjectProperty', target.__$$remotePtr, key); -+ return true; -+ }, -+ has: (target, key) => { -+ if ([ '__$$pointer', '__$$pointerType' ].includes(key)) { -+ return true; -+ } -+ -+ return _commandHandler('hasObjectKey', target.__$$remotePtr, key); -+ }, -+ ownKeys: (target) => _commandHandler('getObjectOwnKeys', target.__$$remotePtr), -+ getOwnPropertyDescriptor: (target, key) => { -+ const desc = _commandHandler('getObjectPropertyDescriptor', target.__$$remotePtr, key); -+ return { -+ configurable: true, -+ ...desc -+ }; -+ } -+}; -+ -+const arrayProxyHandler = { -+ get: (target, key) => { -+ if ([ 'pop', 'shift', 'push', 'unshift' ].includes(key)) { -+ return (...args) => { -+ _commandHandler('performArrayOperation', key, target.__$$pointer, serialize(args)); -+ return target[key](...args); -+ }; -+ } -+ -+ return target[key]; -+ }, -+ set: (target, key, value) => { -+ _commandHandler('performArrayOperation', 'set', target.__$$pointer, [ key, serialize(value) ]); -+ target[key] = value; -+ return true; -+ } -+}; -+ -+const mainWorldClassMap = {}; -+function deserialize (obj, seen = new WeakMap()) { -+ if (isCloneable(obj)) { -+ return obj; -+ } -+ -+ if (obj.$$type === '$symbol') { -+ return Symbol.for(obj.value); -+ } -+ -+ if (obj instanceof Promise) { -+ return obj.then((v) => deserialize(v)); -+ } -+ -+ if (obj.$$type === '$pointer') { -+ if (isMainWorld) { -+ const res = readPointer(obj.ptr); -+ if (obj.type === 'class') { -+ if (!mainWorldClassMap[obj.ref]) { -+ class Klass extends res { -+ constructor (...args) { -+ super(...args); -+ const ptr = allocatePointer(this); -+ const res = obj.construct(serialize(args), ptr); -+ Object.assign(this, deserialize(res)); -+ } -+ } -+ -+ for (const mth in obj.props) { -+ if (mth in obj.props) { -+ Klass.prototype[mth] = deserialize(obj.props[mth]); -+ } -+ } -+ -+ mainWorldClassMap[obj.ref] = Klass; -+ } -+ -+ return mainWorldClassMap[obj.ref]; -+ } -+ -+ return res; -+ } -+ -+ const { ptr } = obj; -+ const cached = getCachedPointer(ptr); -+ if (cached) { -+ return cached; -+ } -+ -+ if (obj.type === 'function') { -+ const props = obj; -+ obj = function (...args) { -+ const thisArg = serialize(this); -+ const preparedArgs = serialize(args); -+ const res = _commandHandler('invokeFunction', props.ptr, thisArg, preparedArgs); -+ return deserialize(res); -+ }; -+ -+ obj.__$$props = props; -+ } -+ -+ if (obj.type === 'class') { -+ const props = obj; -+ obj = class { -+ constructor (...args) { -+ if (this.__$$marker === PTR_MARKER) { -+ // eslint-disable-next-line prefer-destructuring -+ this.__$$remotePtr = this.__$$ptr; -+ delete this.__$$marker; -+ delete this.__$$ptr; -+ } else { -+ const preparedArgs = serialize(args); -+ const res = _commandHandler('instantiateClass', props.ptr, preparedArgs); -+ this.__$$remotePtr = res.ptr; -+ } -+ -+ const proxy = new Proxy(this, classProxyHandler); -+ let proto = Reflect.getPrototypeOf(this); -+ while (proto && proto !== Object.prototype) { -+ for (const key of Reflect.ownKeys(proto)) { -+ if ([ 'constructor', '__$$remotePrototype', '__$$remotePtr' ].includes(key)) { -+ continue; -+ } -+ -+ proxy[key] = this[key]; -+ } -+ -+ proto = Reflect.getPrototypeOf(proto); -+ } -+ -+ return proxy; -+ } -+ }; -+ -+ const prototype = deserialize(_commandHandler('getObjectProperty', props.ptr, 'prototype')); -+ for (const mth of Reflect.ownKeys(prototype)) { -+ if ([ 'constructor', '__$$remotePrototype' ].includes(mth)) { -+ continue; -+ } -+ -+ if (!(mth in prototype)) { -+ continue; -+ } -+ -+ Object.defineProperty(obj.prototype, mth, { -+ configurable: true, -+ enumerable: true, -+ get: () => { -+ const val = prototype[mth]; -+ if (typeof val === 'function') { -+ const fn = function (...args) { -+ const preparedArgs = serialize(args); -+ const res = _commandHandler('invokeFunction', val.__$$pointer, serialize(this), preparedArgs); -+ return deserialize(res); -+ }; -+ -+ fn.toString = val.toString.bind(val); -+ return fn; -+ } -+ -+ return val; -+ }, -+ set: (val) => { -+ if ('set' in Object.getOwnPropertyDescriptor(prototype, mth)) { -+ prototype[mth] = val; -+ } -+ } -+ }); -+ } -+ -+ obj.__$$props = props; -+ obj.__$$isRemote = true; -+ } -+ -+ const proxy = new Proxy(obj, pointerProxyHandler); -+ cachePointer(ptr, proxy); -+ usePointer(proxy, ptr); -+ return proxy; -+ } -+ -+ if (obj.$$type === '$pointerRender') { -+ return readPointer(obj.ptr); -+ } -+ -+ if (obj.$$type === '$array') { -+ const { ptr } = obj; -+ const cached = getCachedPointer(ptr); -+ if (cached) { -+ return cached; -+ } -+ -+ const res = obj.data.map((v) => deserialize(v, seen)); -+ res.__$$pointer = obj.ptr; -+ -+ const proxy = new Proxy(res, arrayProxyHandler); -+ cachePointer(ptr, proxy); -+ usePointer(proxy, ptr); -+ return proxy; -+ } -+ -+ if (obj.$$type === '$window') { -+ return window; -+ } -+ -+ if (obj.$$type === '$document') { -+ return document; -+ } -+ -+ if (obj.$$type === '$html') { -+ const element = document.querySelector(`[data-powercord-serialize-id="${obj.ptr}"]`); -+ element.removeAttribute('data-powercord-serialize-id'); -+ return element; -+ } -+ -+ if (obj.$$type === '$function') { -+ const cached = getCachedFunction(obj.ref); -+ if (cached) { -+ return cached; -+ } -+ -+ const { fn, str } = obj; -+ const wrapper = function (...args) { -+ const thisArg = serialize(this); -+ const preparedArgs = args.map((arg) => serialize(arg)); -+ const res = fn(thisArg, ...preparedArgs); -+ return deserialize(res); -+ }; -+ -+ wrapper.toString = () => str; -+ cacheFunction(obj.ref, wrapper); -+ return wrapper; -+ } -+ -+ if (typeof obj === 'function') { -+ return function (...args) { -+ const thisArg = serialize(this); -+ const preparedArgs = args.map((arg) => serialize(arg)); -+ const res = obj(thisArg, ...preparedArgs); -+ return deserialize(res); -+ }; -+ } -+ -+ if (Array.isArray(obj)) { -+ const res = []; -+ seen.set(obj, res); -+ for (const v of obj) { -+ res.push(deserialize(v, seen)); -+ } -+ -+ Object.defineProperty(res, '__$$pure', { -+ enumerable: false, -+ configurable: false, -+ writable: false, -+ value: true -+ }); -+ -+ return res; -+ } -+ -+ if (obj instanceof Set) { -+ const res = new Set(); -+ for (const item of obj) { -+ res.add(deserialize(item, seen)); -+ } -+ -+ return res; -+ } -+ -+ if (obj instanceof Map) { -+ const res = new Map(); -+ for (const k in obj) { -+ if (k in obj) { -+ res.set(k, deserialize(res[k], seen)); -+ } -+ } -+ -+ return res; -+ } -+ -+ if (seen.has(obj)) { -+ return seen.get(obj); -+ } -+ -+ const res = {}; -+ let ptr = null; -+ if (Reflect.has(obj, '__$$pointerRender')) { -+ ptr = obj.__$$pointerRender; -+ const cached = getCachedPointer(ptr); -+ if (cached) { -+ return cached; -+ } -+ -+ obj = obj.getData(); -+ mainWorldPointers.set(res, ptr); -+ usePointer(res, ptr); -+ } -+ -+ seen.set(obj, res); -+ for (const key of Reflect.ownKeys(obj)) { -+ if (key === '__$$pointerRender') { -+ continue; -+ } -+ -+ const val = obj[key]; -+ if (val?.$$type === 'desc') { -+ const fakeDesc = { -+ configurable: val.desc.configurable, -+ enumerable: val.desc.enumerable, -+ get: () => deserialize(val.desc.get()) -+ }; -+ -+ Object.defineProperty(res, key, fakeDesc); -+ } -+ -+ res[key] = deserialize(val, seen); -+ } -+ -+ Object.defineProperty(res, '__$$pure', { -+ enumerable: false, -+ configurable: false, -+ writable: false, -+ value: true -+ }); -+ -+ if (ptr !== null) { -+ cachePointer(ptr, res); -+ } -+ -+ return res; -+} -+ -+ -+// -- -+// Exports if in renderer -+// -- -+if (!isMainWorld) { -+ module.exports = { -+ serialize, -+ deserialize, -+ usePointer, -+ freePointer, -+ setCommandHandler: (h) => (_commandHandler = h) -+ }; -+ -+ if (EXPOSE_DEBUGGING_HELPER) { -+ let passed = []; -+ require('electron').contextBridge.exposeInMainWorld('0$$SerializeDebuggerHelper', { -+ pass: (v) => passed.push(deserialize(v)), -+ get: () => passed.map((v) => serialize(v)), -+ eq: () => passed.every((v) => v === passed[0]), -+ log: () => console.log(passed), -+ clear: () => passed = [] -+ }); -+ -+ window.__$$SerializeDebuggerHelper = Object.create(null); -+ window.__$$SerializeDebuggerHelper.push = (...args) => passed.push(...args); -+ window.__$$SerializeDebuggerHelper.get = () => [ ...passed ]; -+ window.__$$SerializeDebuggerHelper.log = () => console.log(passed); -+ window.__$$SerializeDebuggerHelper.eq = () => passed.every((v) => v === passed[0]); -+ window.__$$SerializeDebuggerHelper.clear = () => passed = []; -+ Object.freeze(window.__$$SerializeDebuggerHelper); -+ } -+} -+ -+if (EXPOSE_DEBUGGING_HELPER) { -+ if (isMainWorld) { -+ window.__$$SerializeDebuggerHelper = Object.create(null); -+ window.__$$SerializeDebuggerHelper.pass = (v) => window['0$$SerializeDebuggerHelper'].pass(serialize(v)); -+ window.__$$SerializeDebuggerHelper.get = () => window['0$$SerializeDebuggerHelper'].get().map((v) => deserialize(v)); -+ window.__$$SerializeDebuggerHelper.eq = () => window['0$$SerializeDebuggerHelper'].eq(); -+ window.__$$SerializeDebuggerHelper.log = () => window['0$$SerializeDebuggerHelper'].log(); -+ window.__$$SerializeDebuggerHelper.clear = () => window['0$$SerializeDebuggerHelper'].clear(); -+ window.__$$SerializeDebuggerHelper.leq = () => { -+ const val = window.__$$SerializeDebuggerHelper.get(); -+ return val.every((v) => v === val[0]); -+ }; -+ -+ Object.freeze(window.__$$SerializeDebuggerHelper); -+ } -+ -+ window.__$$SerializeMemoryArea = Object.create(null); -+ window.__$$SerializeMemoryArea.memorySpace = memorySpace; -+ window.__$$SerializeMemoryArea.usedPointers = usedPointers; -+ window.__$$SerializeMemoryArea.localPointerProperties = localPointerProperties; -+ window.__$$SerializeMemoryArea.pointerCache = pointerCache; -+ window.__$$SerializeMemoryArea.functionWrappers = functionCache; -+ Object.freeze(window.__$$SerializeMemoryArea); -+} |