mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-02-23 23:15:10 +00:00
Merge branch 'dev' into managed-styles-rewrite
This commit is contained in:
commit
0d4e6795e0
58 changed files with 1702 additions and 1959 deletions
42
.github/ISSUE_TEMPLATE/blank.yml
vendored
42
.github/ISSUE_TEMPLATE/blank.yml
vendored
|
@ -2,30 +2,24 @@ name: Blank Issue
|
||||||
description: Create a blank issue. ALWAYS FIRST USE OUR SUPPORT CHANNEL! ONLY USE THIS FORM IF YOU ARE A CONTRIBUTOR OR WERE TOLD TO DO SO IN THE SUPPORT CHANNEL.
|
description: Create a blank issue. ALWAYS FIRST USE OUR SUPPORT CHANNEL! ONLY USE THIS FORM IF YOU ARE A CONTRIBUTOR OR WERE TOLD TO DO SO IN THE SUPPORT CHANNEL.
|
||||||
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
# READ THIS BEFORE OPENING AN ISSUE
|

|
||||||
|
|
||||||
This form is ONLY FOR DEVELOPERS. YOUR ISSUE WILL BE CLOSED AND YOU WILL POSSIBLY BE BLOCKED FROM THE REPOSITORY IF YOU IGNORE THIS.
|
GitHub Issues are for development, not support! Please use our [support server](https://vencord.dev/discord) unless you are a Vencord Developer.
|
||||||
|
|
||||||
DO NOT USE THIS FORM, unless
|
|
||||||
- you are a vencord contributor
|
|
||||||
- you were given explicit permission to use this form by a moderator in our support server
|
|
||||||
|
|
||||||
DO NOT USE THIS FORM FOR SECURITY RELATED ISSUES. [CREATE A SECURITY ADVISORY INSTEAD.](https://github.com/Vendicated/Vencord/security/advisories/new)
|
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: content
|
id: content
|
||||||
attributes:
|
attributes:
|
||||||
label: Content
|
label: Content
|
||||||
validations:
|
validations:
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: checkboxes
|
|
||||||
id: agreement-check
|
|
||||||
attributes:
|
|
||||||
label: Request Agreement
|
|
||||||
options:
|
|
||||||
- label: I have read the requirements for opening an issue above
|
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: agreement-check
|
||||||
|
attributes:
|
||||||
|
label: Request Agreement
|
||||||
|
options:
|
||||||
|
- label: I have read the requirements for opening an issue above
|
||||||
|
required: true
|
||||||
|
|
127
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
127
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
@ -4,78 +4,63 @@ labels: [bug]
|
||||||
title: "[Bug] <title>"
|
title: "[Bug] <title>"
|
||||||
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
# READ THIS BEFORE OPENING AN ISSUE
|

|
||||||
|
|
||||||
This form is ONLY FOR DEVELOPERS. YOUR ISSUE WILL BE CLOSED AND YOU WILL POSSIBLY BE BLOCKED FROM THE REPOSITORY IF YOU IGNORE THIS.
|
GitHub Issues are for development, not support! Please use our [support server](https://vencord.dev/discord) unless you are a Vencord Developer.
|
||||||
|
|
||||||
DO NOT USE THIS FORM, unless
|
|
||||||
- you are a vencord contributor
|
|
||||||
- you were given explicit permission to use this form by a moderator in our support server
|
|
||||||
|
|
||||||
DO NOT USE THIS FORM FOR SECURITY RELATED ISSUES. [CREATE A SECURITY ADVISORY INSTEAD.](https://github.com/Vendicated/Vencord/security/advisories/new)
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
id: discord
|
|
||||||
attributes:
|
|
||||||
label: Discord Account
|
|
||||||
description: Who on Discord is making this request? Not required but encouraged for easier follow-up
|
|
||||||
placeholder: username#0000
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: bug-description
|
id: bug-description
|
||||||
attributes:
|
attributes:
|
||||||
label: What happens when the bug or crash occurs?
|
label: What happens when the bug or crash occurs?
|
||||||
description: Where does this bug or crash occur, when does it occur, etc.
|
description: Where does this bug or crash occur, when does it occur, etc.
|
||||||
placeholder: The bug/crash happens sometimes when I do ..., causing this to not work/the app to crash. I think it happens because of ...
|
placeholder: The bug/crash happens sometimes when I do ..., causing this to not work/the app to crash. I think it happens because of ...
|
||||||
validations:
|
validations:
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: expected-behaviour
|
|
||||||
attributes:
|
|
||||||
label: What is the expected behaviour?
|
|
||||||
description: Simply detail what the expected behaviour is.
|
|
||||||
placeholder: I expect Vencord/Discord to open the ... page instead of ..., it prevents me from doing ...
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: steps-to-take
|
|
||||||
attributes:
|
|
||||||
label: How do you recreate this bug or crash?
|
|
||||||
description: Give us a list of steps in order to recreate the bug or crash.
|
|
||||||
placeholder: |
|
|
||||||
1. Do ...
|
|
||||||
2. Then ...
|
|
||||||
3. Do this ..., ... and then ...
|
|
||||||
4. Observe "the bug" or "the crash"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: crash-log
|
|
||||||
attributes:
|
|
||||||
label: Errors
|
|
||||||
description: Open the Developer Console with Ctrl/Cmd + Shift + i. Then look for any red errors (Ignore network errors like Failed to load resource) and paste them between the "```".
|
|
||||||
value: |
|
|
||||||
```
|
|
||||||
Replace this text with your crash-log.
|
|
||||||
```
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: checkboxes
|
|
||||||
id: agreement-check
|
|
||||||
attributes:
|
|
||||||
label: Request Agreement
|
|
||||||
description: We only accept reports for bugs that happen on Discord Stable. Canary and PTB are Development branches and may be unstable
|
|
||||||
options:
|
|
||||||
- label: I am using Discord Stable or tried on Stable and this bug happens there as well
|
|
||||||
required: true
|
required: true
|
||||||
- label: I have read the requirements for opening an issue above
|
|
||||||
|
- type: textarea
|
||||||
|
id: expected-behaviour
|
||||||
|
attributes:
|
||||||
|
label: What is the expected behaviour?
|
||||||
|
description: Simply detail what the expected behaviour is.
|
||||||
|
placeholder: I expect Vencord/Discord to open the ... page instead of ..., it prevents me from doing ...
|
||||||
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: steps-to-take
|
||||||
|
attributes:
|
||||||
|
label: How do you recreate this bug or crash?
|
||||||
|
description: Give us a list of steps in order to recreate the bug or crash.
|
||||||
|
placeholder: |
|
||||||
|
1. Do ...
|
||||||
|
2. Then ...
|
||||||
|
3. Do this ..., ... and then ...
|
||||||
|
4. Observe "the bug" or "the crash"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: crash-log
|
||||||
|
attributes:
|
||||||
|
label: Errors
|
||||||
|
description: Open the Developer Console with Ctrl/Cmd + Shift + i. Then look for any red errors (Ignore network errors like Failed to load resource) and paste them between the "```".
|
||||||
|
value: |
|
||||||
|
```
|
||||||
|
Replace this text with your crash-log.
|
||||||
|
```
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: agreement-check
|
||||||
|
attributes:
|
||||||
|
label: Request Agreement
|
||||||
|
description: We only accept reports for bugs that happen on Discord Stable. Canary and PTB are Development branches and may be unstable
|
||||||
|
options:
|
||||||
|
- label: I am using Discord Stable or tried on Stable and this bug happens there as well
|
||||||
|
required: true
|
||||||
|
- label: I am a Vencord Developer
|
||||||
|
required: true
|
||||||
|
|
BIN
.github/ISSUE_TEMPLATE/developer-banner.png
vendored
Normal file
BIN
.github/ISSUE_TEMPLATE/developer-banner.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -42,7 +42,7 @@ jobs:
|
||||||
|
|
||||||
- name: Clean up obsolete files
|
- name: Clean up obsolete files
|
||||||
run: |
|
run: |
|
||||||
rm -rf dist/*-unpacked dist/monaco Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map
|
rm -rf dist/*-unpacked dist/vendor Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map
|
||||||
|
|
||||||
- name: Get some values needed for the release
|
- name: Get some values needed for the release
|
||||||
id: release_values
|
id: release_values
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
|
|
||||||
"web_accessible_resources": [
|
"web_accessible_resources": [
|
||||||
{
|
{
|
||||||
"resources": ["dist/*", "third-party/*"],
|
"resources": ["dist/*", "vendor/*"],
|
||||||
"matches": ["*://*.discord.com/*"]
|
"matches": ["*://*.discord.com/*"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -15,7 +15,7 @@ declare global {
|
||||||
const getTheme: () => string;
|
const getTheme: () => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BASE = "/dist/monaco/vs";
|
const BASE = "/vendor/monaco/vs";
|
||||||
|
|
||||||
self.MonacoEnvironment = {
|
self.MonacoEnvironment = {
|
||||||
getWorkerUrl(_moduleId: unknown, label: string) {
|
getWorkerUrl(_moduleId: unknown, label: string) {
|
||||||
|
|
|
@ -24,12 +24,12 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const script = document.createElement("script");
|
const script = document.createElement("script");
|
||||||
script.src = new URL("/dist/monaco/index.js", baseUrl);
|
script.src = new URL("/vendor/monaco/index.js", baseUrl);
|
||||||
|
|
||||||
const style = document.createElement("link");
|
const style = document.createElement("link");
|
||||||
style.type = "text/css";
|
style.type = "text/css";
|
||||||
style.rel = "stylesheet";
|
style.rel = "stylesheet";
|
||||||
style.href = new URL("/dist/monaco/index.css", baseUrl);
|
style.href = new URL("/vendor/monaco/index.css", baseUrl);
|
||||||
|
|
||||||
document.body.append(style, script);
|
document.body.append(style, script);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -134,7 +134,7 @@ export default tseslint.config(
|
||||||
"no-unsafe-optional-chaining": "error",
|
"no-unsafe-optional-chaining": "error",
|
||||||
"no-useless-backreference": "error",
|
"no-useless-backreference": "error",
|
||||||
"use-isnan": "error",
|
"use-isnan": "error",
|
||||||
"prefer-const": "error",
|
"prefer-const": ["error", { destructuring: "all" }],
|
||||||
"prefer-spread": "error",
|
"prefer-spread": "error",
|
||||||
|
|
||||||
// Plugin Rules
|
// Plugin Rules
|
||||||
|
|
49
package.json
49
package.json
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.11.4",
|
"version": "1.11.5",
|
||||||
"description": "The cutest Discord client mod",
|
"description": "The cutest Discord client mod",
|
||||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
@ -24,19 +24,18 @@
|
||||||
"dev": "pnpm watch",
|
"dev": "pnpm watch",
|
||||||
"watchWeb": "pnpm buildWeb --watch",
|
"watchWeb": "pnpm buildWeb --watch",
|
||||||
"generatePluginJson": "tsx scripts/generatePluginList.ts",
|
"generatePluginJson": "tsx scripts/generatePluginList.ts",
|
||||||
"generateTypes": "tspc --emitDeclarationOnly --declaration --outDir packages/vencord-types",
|
"generateTypes": "tspc --emitDeclarationOnly --declaration --outDir packages/vencord-types --allowJs false",
|
||||||
"inject": "node scripts/runInstaller.mjs",
|
"inject": "node scripts/runInstaller.mjs",
|
||||||
"uninject": "node scripts/runInstaller.mjs",
|
"uninject": "node scripts/runInstaller.mjs",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
"lint-styles": "stylelint \"src/**/*.css\" --ignore-pattern src/userplugins",
|
"lint-styles": "stylelint \"src/**/*.css\" --ignore-pattern src/userplugins",
|
||||||
"lint:fix": "pnpm lint --fix",
|
"lint:fix": "pnpm lint --fix",
|
||||||
"test": "pnpm buildStandalone && pnpm lint && pnpm lint-styles && pnpm testTsc && pnpm generatePluginJson",
|
"test": "pnpm buildStandalone && pnpm testTsc && pnpm lint && pnpm lint-styles && pnpm generatePluginJson",
|
||||||
"testWeb": "pnpm lint && pnpm buildWeb && pnpm testTsc",
|
"testWeb": "pnpm lint && pnpm buildWeb && pnpm testTsc",
|
||||||
"testTsc": "tsc --noEmit"
|
"testTsc": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@intrnl/xxhash64": "^0.1.2",
|
"@intrnl/xxhash64": "^0.1.2",
|
||||||
"@sapphi-red/web-noise-suppressor": "0.3.5",
|
|
||||||
"@vap/core": "0.0.12",
|
"@vap/core": "0.0.12",
|
||||||
"@vap/shiki": "0.10.5",
|
"@vap/shiki": "0.10.5",
|
||||||
"fflate": "^0.8.2",
|
"fflate": "^0.8.2",
|
||||||
|
@ -46,31 +45,31 @@
|
||||||
"virtual-merge": "^1.0.1"
|
"virtual-merge": "^1.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@stylistic/eslint-plugin": "^2.12.1",
|
"@stylistic/eslint-plugin": "^4.0.0",
|
||||||
"@types/chrome": "^0.0.287",
|
"@types/chrome": "^0.0.304",
|
||||||
"@types/diff": "^6.0.0",
|
"@types/diff": "^7.0.1",
|
||||||
"@types/lodash": "^4.17.14",
|
"@types/lodash": "^4.17.14",
|
||||||
"@types/node": "^22.10.5",
|
"@types/node": "^22.10.5",
|
||||||
"@types/react": "^19.0.2",
|
"@types/react": "^19.0.10",
|
||||||
"@types/react-dom": "^19.0.2",
|
"@types/react-dom": "^19.0.4",
|
||||||
"@types/yazl": "^2.4.5",
|
"@types/yazl": "^2.4.5",
|
||||||
"diff": "^7.0.0",
|
"diff": "^7.0.0",
|
||||||
"discord-types": "^1.3.26",
|
"discord-types": "^1.3.26",
|
||||||
"esbuild": "^0.15.18",
|
"esbuild": "^0.25.0",
|
||||||
"eslint": "^9.17.0",
|
"eslint": "^9.20.1",
|
||||||
"eslint-import-resolver-alias": "^1.1.2",
|
"eslint-import-resolver-alias": "^1.1.2",
|
||||||
"eslint-plugin-path-alias": "2.1.0",
|
"eslint-plugin-path-alias": "2.1.0",
|
||||||
"eslint-plugin-react": "^7.37.3",
|
"eslint-plugin-react": "^7.37.3",
|
||||||
"eslint-plugin-simple-header": "^1.2.1",
|
"eslint-plugin-simple-header": "^1.2.1",
|
||||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||||
"eslint-plugin-unused-imports": "^4.1.4",
|
"eslint-plugin-unused-imports": "^4.1.4",
|
||||||
"highlight.js": "11.7.0",
|
"highlight.js": "11.11.1",
|
||||||
"html-minifier-terser": "^7.2.0",
|
"html-minifier-terser": "^7.2.0",
|
||||||
"moment": "^2.22.2",
|
"moment": "^2.22.2",
|
||||||
"puppeteer-core": "^23.11.1",
|
"puppeteer-core": "^24.2.1",
|
||||||
"standalone-electron-types": "^1.0.0",
|
"standalone-electron-types": "^34.2.0",
|
||||||
"stylelint": "^16.12.0",
|
"stylelint": "^16.12.0",
|
||||||
"stylelint-config-standard": "^36.0.1",
|
"stylelint-config-standard": "^37.0.0",
|
||||||
"ts-patch": "^3.3.0",
|
"ts-patch": "^3.3.0",
|
||||||
"ts-pattern": "^5.6.0",
|
"ts-pattern": "^5.6.0",
|
||||||
"tsx": "^4.19.2",
|
"tsx": "^4.19.2",
|
||||||
|
@ -80,10 +79,10 @@
|
||||||
"typescript-transform-paths": "^3.5.3",
|
"typescript-transform-paths": "^3.5.3",
|
||||||
"zip-local": "^0.3.5"
|
"zip-local": "^0.3.5"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.1.0",
|
"packageManager": "pnpm@10.4.1",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
"eslint@9.17.0": "patches/eslint@9.17.0.patch",
|
"eslint@9.20.1": "patches/eslint@9.20.1.patch",
|
||||||
"eslint-plugin-path-alias@2.1.0": "patches/eslint-plugin-path-alias@2.1.0.patch"
|
"eslint-plugin-path-alias@2.1.0": "patches/eslint-plugin-path-alias@2.1.0.patch"
|
||||||
},
|
},
|
||||||
"peerDependencyRules": {
|
"peerDependencyRules": {
|
||||||
|
@ -96,18 +95,14 @@
|
||||||
"source-map-resolve": "*",
|
"source-map-resolve": "*",
|
||||||
"resolve-url": "*",
|
"resolve-url": "*",
|
||||||
"source-map-url": "*",
|
"source-map-url": "*",
|
||||||
"urix": "*"
|
"urix": "*",
|
||||||
}
|
"q": "*"
|
||||||
},
|
|
||||||
"webExt": {
|
|
||||||
"artifactsDir": "./dist",
|
|
||||||
"build": {
|
|
||||||
"overwriteDest": true
|
|
||||||
},
|
},
|
||||||
"sourceDir": "./dist/firefox-unpacked"
|
"onlyBuiltDependencies": [
|
||||||
|
"esbuild"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18",
|
"node": ">=18"
|
||||||
"pnpm": ">=9"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@vencord/types",
|
"name": "@vencord/types",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "0.1.3",
|
"version": "1.11.5",
|
||||||
"description": "",
|
"description": "",
|
||||||
"types": "index.d.ts",
|
"types": "index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -13,16 +13,16 @@
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/fs-extra": "^11.0.4",
|
"@types/fs-extra": "^11.0.4",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.3.0",
|
||||||
"tsx": "^3.12.6"
|
"tsx": "^4.19.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/lodash": "^4.14.191",
|
"@types/lodash": "4.17.15",
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^22.13.4",
|
||||||
"@types/react": "^18.2.0",
|
"@types/react": "18.3.1",
|
||||||
"@types/react-dom": "^18.0.10",
|
"@types/react-dom": "18.3.1",
|
||||||
"discord-types": "^1.3.26",
|
"discord-types": "^1.3.26",
|
||||||
"standalone-electron-types": "^1.0.0",
|
"standalone-electron-types": "^34.2.0",
|
||||||
"type-fest": "^3.5.3"
|
"type-fest": "^4.35.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1922
pnpm-lock.yaml
generated
1922
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -17,38 +17,41 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import esbuild from "esbuild";
|
// @ts-check
|
||||||
|
|
||||||
import { readdir } from "fs/promises";
|
import { readdir } from "fs/promises";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
|
||||||
import { BUILD_TIMESTAMP, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_STANDALONE, IS_UPDATER_DISABLED, resolvePluginName, VERSION, commonRendererPlugins, watch } from "./common.mjs";
|
import { BUILD_TIMESTAMP, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_STANDALONE, IS_UPDATER_DISABLED, resolvePluginName, VERSION, commonRendererPlugins, watch, buildOrWatchAll, stringifyValues } from "./common.mjs";
|
||||||
|
|
||||||
const defines = {
|
const defines = stringifyValues({
|
||||||
IS_STANDALONE,
|
IS_STANDALONE,
|
||||||
IS_DEV,
|
IS_DEV,
|
||||||
IS_REPORTER,
|
IS_REPORTER,
|
||||||
IS_UPDATER_DISABLED,
|
IS_UPDATER_DISABLED,
|
||||||
IS_WEB: false,
|
IS_WEB: false,
|
||||||
IS_EXTENSION: false,
|
IS_EXTENSION: false,
|
||||||
VERSION: JSON.stringify(VERSION),
|
VERSION,
|
||||||
BUILD_TIMESTAMP
|
BUILD_TIMESTAMP
|
||||||
};
|
});
|
||||||
|
|
||||||
if (defines.IS_STANDALONE === false)
|
if (defines.IS_STANDALONE === "false") {
|
||||||
// If this is a local build (not standalone), optimize
|
// If this is a local build (not standalone), optimize
|
||||||
// for the specific platform we're on
|
// for the specific platform we're on
|
||||||
defines["process.platform"] = JSON.stringify(process.platform);
|
defines["process.platform"] = JSON.stringify(process.platform);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {esbuild.BuildOptions}
|
* @type {import("esbuild").BuildOptions}
|
||||||
*/
|
*/
|
||||||
const nodeCommonOpts = {
|
const nodeCommonOpts = {
|
||||||
...commonOpts,
|
...commonOpts,
|
||||||
|
define: defines,
|
||||||
format: "cjs",
|
format: "cjs",
|
||||||
platform: "node",
|
platform: "node",
|
||||||
target: ["esnext"],
|
target: ["esnext"],
|
||||||
external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external],
|
// @ts-ignore this is never undefined
|
||||||
define: defines
|
external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external]
|
||||||
};
|
};
|
||||||
|
|
||||||
const sourceMapFooter = s => watch ? "" : `//# sourceMappingURL=vencord://${s}.js.map`;
|
const sourceMapFooter = s => watch ? "" : `//# sourceMappingURL=vencord://${s}.js.map`;
|
||||||
|
@ -102,25 +105,27 @@ const globNativesPlugin = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
await Promise.all([
|
/** @type {import("esbuild").BuildOptions[]} */
|
||||||
|
const buildConfigs = ([
|
||||||
// Discord Desktop main & renderer & preload
|
// Discord Desktop main & renderer & preload
|
||||||
esbuild.build({
|
{
|
||||||
...nodeCommonOpts,
|
...nodeCommonOpts,
|
||||||
entryPoints: ["src/main/index.ts"],
|
entryPoints: ["src/main/index.ts"],
|
||||||
outfile: "dist/patcher.js",
|
outfile: "dist/patcher.js",
|
||||||
footer: { js: "//# sourceURL=VencordPatcher\n" + sourceMapFooter("patcher") },
|
footer: { js: "//# sourceURL=VencordPatcher\n" + sourceMapFooter("patcher") },
|
||||||
sourcemap,
|
sourcemap,
|
||||||
define: {
|
|
||||||
...defines,
|
|
||||||
IS_DISCORD_DESKTOP: true,
|
|
||||||
IS_VESKTOP: false
|
|
||||||
},
|
|
||||||
plugins: [
|
plugins: [
|
||||||
|
// @ts-ignore this is never undefined
|
||||||
...nodeCommonOpts.plugins,
|
...nodeCommonOpts.plugins,
|
||||||
globNativesPlugin
|
globNativesPlugin
|
||||||
]
|
],
|
||||||
}),
|
define: {
|
||||||
esbuild.build({
|
...defines,
|
||||||
|
IS_DISCORD_DESKTOP: "true",
|
||||||
|
IS_VESKTOP: "false"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
...commonOpts,
|
...commonOpts,
|
||||||
entryPoints: ["src/Vencord.ts"],
|
entryPoints: ["src/Vencord.ts"],
|
||||||
outfile: "dist/renderer.js",
|
outfile: "dist/renderer.js",
|
||||||
|
@ -135,11 +140,11 @@ await Promise.all([
|
||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_DISCORD_DESKTOP: true,
|
IS_DISCORD_DESKTOP: "true",
|
||||||
IS_VESKTOP: false
|
IS_VESKTOP: "false"
|
||||||
}
|
}
|
||||||
}),
|
},
|
||||||
esbuild.build({
|
{
|
||||||
...nodeCommonOpts,
|
...nodeCommonOpts,
|
||||||
entryPoints: ["src/preload.ts"],
|
entryPoints: ["src/preload.ts"],
|
||||||
outfile: "dist/preload.js",
|
outfile: "dist/preload.js",
|
||||||
|
@ -147,29 +152,29 @@ await Promise.all([
|
||||||
sourcemap,
|
sourcemap,
|
||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_DISCORD_DESKTOP: true,
|
IS_DISCORD_DESKTOP: "true",
|
||||||
IS_VESKTOP: false
|
IS_VESKTOP: "false"
|
||||||
}
|
}
|
||||||
}),
|
},
|
||||||
|
|
||||||
// Vencord Desktop main & renderer & preload
|
// Vencord Desktop main & renderer & preload
|
||||||
esbuild.build({
|
{
|
||||||
...nodeCommonOpts,
|
...nodeCommonOpts,
|
||||||
entryPoints: ["src/main/index.ts"],
|
entryPoints: ["src/main/index.ts"],
|
||||||
outfile: "dist/vencordDesktopMain.js",
|
outfile: "dist/vencordDesktopMain.js",
|
||||||
footer: { js: "//# sourceURL=VencordDesktopMain\n" + sourceMapFooter("vencordDesktopMain") },
|
footer: { js: "//# sourceURL=VencordDesktopMain\n" + sourceMapFooter("vencordDesktopMain") },
|
||||||
sourcemap,
|
sourcemap,
|
||||||
define: {
|
|
||||||
...defines,
|
|
||||||
IS_DISCORD_DESKTOP: false,
|
|
||||||
IS_VESKTOP: true
|
|
||||||
},
|
|
||||||
plugins: [
|
plugins: [
|
||||||
...nodeCommonOpts.plugins,
|
...nodeCommonOpts.plugins,
|
||||||
globNativesPlugin
|
globNativesPlugin
|
||||||
]
|
],
|
||||||
}),
|
define: {
|
||||||
esbuild.build({
|
...defines,
|
||||||
|
IS_DISCORD_DESKTOP: "false",
|
||||||
|
IS_VESKTOP: "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
...commonOpts,
|
...commonOpts,
|
||||||
entryPoints: ["src/Vencord.ts"],
|
entryPoints: ["src/Vencord.ts"],
|
||||||
outfile: "dist/vencordDesktopRenderer.js",
|
outfile: "dist/vencordDesktopRenderer.js",
|
||||||
|
@ -184,11 +189,11 @@ await Promise.all([
|
||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_DISCORD_DESKTOP: false,
|
IS_DISCORD_DESKTOP: "false",
|
||||||
IS_VESKTOP: true
|
IS_VESKTOP: "true"
|
||||||
}
|
}
|
||||||
}),
|
},
|
||||||
esbuild.build({
|
{
|
||||||
...nodeCommonOpts,
|
...nodeCommonOpts,
|
||||||
entryPoints: ["src/preload.ts"],
|
entryPoints: ["src/preload.ts"],
|
||||||
outfile: "dist/vencordDesktopPreload.js",
|
outfile: "dist/vencordDesktopPreload.js",
|
||||||
|
@ -196,14 +201,10 @@ await Promise.all([
|
||||||
sourcemap,
|
sourcemap,
|
||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_DISCORD_DESKTOP: false,
|
IS_DISCORD_DESKTOP: "false",
|
||||||
IS_VESKTOP: true
|
IS_VESKTOP: "true"
|
||||||
}
|
}
|
||||||
}),
|
}
|
||||||
]).catch(err => {
|
]);
|
||||||
console.error("Build failed");
|
|
||||||
console.error(err.message);
|
await buildOrWatchAll(buildConfigs);
|
||||||
// make ci fail
|
|
||||||
if (!commonOpts.watch)
|
|
||||||
process.exitCode = 1;
|
|
||||||
});
|
|
||||||
|
|
|
@ -17,29 +17,30 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import esbuild from "esbuild";
|
// @ts-check
|
||||||
|
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { appendFile, mkdir, readdir, readFile, rm, writeFile } from "fs/promises";
|
import { appendFile, mkdir, readdir, readFile, rm, writeFile } from "fs/promises";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import Zip from "zip-local";
|
import Zip from "zip-local";
|
||||||
|
|
||||||
import { BUILD_TIMESTAMP, commonOpts, globPlugins, IS_DEV, IS_REPORTER, VERSION, commonRendererPlugins } from "./common.mjs";
|
import { BUILD_TIMESTAMP, commonOpts, globPlugins, IS_DEV, IS_REPORTER, VERSION, commonRendererPlugins, buildOrWatchAll, stringifyValues } from "./common.mjs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {esbuild.BuildOptions}
|
* @type {import("esbuild").BuildOptions}
|
||||||
*/
|
*/
|
||||||
const commonOptions = {
|
const commonOptions = {
|
||||||
...commonOpts,
|
...commonOpts,
|
||||||
entryPoints: ["browser/Vencord.ts"],
|
entryPoints: ["browser/Vencord.ts"],
|
||||||
globalName: "Vencord",
|
|
||||||
format: "iife",
|
format: "iife",
|
||||||
|
globalName: "Vencord",
|
||||||
external: ["~plugins", "~git-hash", "/assets/*"],
|
external: ["~plugins", "~git-hash", "/assets/*"],
|
||||||
|
target: ["esnext"],
|
||||||
plugins: [
|
plugins: [
|
||||||
globPlugins("web"),
|
globPlugins("web"),
|
||||||
...commonRendererPlugins
|
...commonRendererPlugins
|
||||||
],
|
],
|
||||||
target: ["esnext"],
|
define: stringifyValues({
|
||||||
define: {
|
|
||||||
IS_WEB: true,
|
IS_WEB: true,
|
||||||
IS_EXTENSION: false,
|
IS_EXTENSION: false,
|
||||||
IS_STANDALONE: true,
|
IS_STANDALONE: true,
|
||||||
|
@ -48,9 +49,9 @@ const commonOptions = {
|
||||||
IS_DISCORD_DESKTOP: false,
|
IS_DISCORD_DESKTOP: false,
|
||||||
IS_VESKTOP: false,
|
IS_VESKTOP: false,
|
||||||
IS_UPDATER_DISABLED: true,
|
IS_UPDATER_DISABLED: true,
|
||||||
VERSION: JSON.stringify(VERSION),
|
VERSION,
|
||||||
BUILD_TIMESTAMP
|
BUILD_TIMESTAMP
|
||||||
}
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
const MonacoWorkerEntryPoints = [
|
const MonacoWorkerEntryPoints = [
|
||||||
|
@ -58,70 +59,59 @@ const MonacoWorkerEntryPoints = [
|
||||||
"vs/editor/editor.worker.js"
|
"vs/editor/editor.worker.js"
|
||||||
];
|
];
|
||||||
|
|
||||||
const RnNoiseFiles = [
|
/** @type {import("esbuild").BuildOptions[]} */
|
||||||
"dist/rnnoise.wasm",
|
const buildConfigs = [
|
||||||
"dist/rnnoise_simd.wasm",
|
{
|
||||||
"dist/rnnoise/workletProcessor.js",
|
entryPoints: MonacoWorkerEntryPoints.map(entry => `node_modules/monaco-editor/esm/${entry}`),
|
||||||
"LICENSE"
|
bundle: true,
|
||||||
|
minify: true,
|
||||||
|
format: "iife",
|
||||||
|
outbase: "node_modules/monaco-editor/esm/",
|
||||||
|
outdir: "dist/vendor/monaco"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
entryPoints: ["browser/monaco.ts"],
|
||||||
|
bundle: true,
|
||||||
|
minify: true,
|
||||||
|
format: "iife",
|
||||||
|
outfile: "dist/vendor/monaco/index.js",
|
||||||
|
loader: {
|
||||||
|
".ttf": "file"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...commonOptions,
|
||||||
|
outfile: "dist/browser.js",
|
||||||
|
footer: { js: "//# sourceURL=VencordWeb" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...commonOptions,
|
||||||
|
outfile: "dist/extension.js",
|
||||||
|
define: {
|
||||||
|
...commonOptions.define,
|
||||||
|
IS_EXTENSION: "true"
|
||||||
|
},
|
||||||
|
footer: { js: "//# sourceURL=VencordWeb" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...commonOptions,
|
||||||
|
inject: ["browser/GMPolyfill.js", ...(commonOptions?.inject || [])],
|
||||||
|
define: {
|
||||||
|
...commonOptions.define,
|
||||||
|
window: "unsafeWindow",
|
||||||
|
},
|
||||||
|
outfile: "dist/Vencord.user.js",
|
||||||
|
banner: {
|
||||||
|
js: readFileSync("browser/userscript.meta.js", "utf-8").replace("%version%", `${VERSION}.${new Date().getTime()}`)
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
// UserScripts get wrapped in an iife, so define Vencord prop on window that returns our local
|
||||||
|
js: "Object.defineProperty(unsafeWindow,'Vencord',{get:()=>Vencord});"
|
||||||
|
}
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
await Promise.all(
|
await buildOrWatchAll(buildConfigs);
|
||||||
[
|
|
||||||
esbuild.build({
|
|
||||||
entryPoints: MonacoWorkerEntryPoints.map(entry => `node_modules/monaco-editor/esm/${entry}`),
|
|
||||||
bundle: true,
|
|
||||||
minify: true,
|
|
||||||
format: "iife",
|
|
||||||
outbase: "node_modules/monaco-editor/esm/",
|
|
||||||
outdir: "dist/monaco"
|
|
||||||
}),
|
|
||||||
esbuild.build({
|
|
||||||
entryPoints: ["browser/monaco.ts"],
|
|
||||||
bundle: true,
|
|
||||||
minify: true,
|
|
||||||
format: "iife",
|
|
||||||
outfile: "dist/monaco/index.js",
|
|
||||||
loader: {
|
|
||||||
".ttf": "file"
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
esbuild.build({
|
|
||||||
...commonOptions,
|
|
||||||
outfile: "dist/browser.js",
|
|
||||||
footer: { js: "//# sourceURL=VencordWeb" }
|
|
||||||
}),
|
|
||||||
esbuild.build({
|
|
||||||
...commonOptions,
|
|
||||||
outfile: "dist/extension.js",
|
|
||||||
define: {
|
|
||||||
...commonOptions?.define,
|
|
||||||
IS_EXTENSION: true,
|
|
||||||
},
|
|
||||||
footer: { js: "//# sourceURL=VencordWeb" }
|
|
||||||
}),
|
|
||||||
esbuild.build({
|
|
||||||
...commonOptions,
|
|
||||||
inject: ["browser/GMPolyfill.js", ...(commonOptions?.inject || [])],
|
|
||||||
define: {
|
|
||||||
...(commonOptions?.define),
|
|
||||||
window: "unsafeWindow",
|
|
||||||
},
|
|
||||||
outfile: "dist/Vencord.user.js",
|
|
||||||
banner: {
|
|
||||||
js: readFileSync("browser/userscript.meta.js", "utf-8").replace("%version%", `${VERSION}.${new Date().getTime()}`)
|
|
||||||
},
|
|
||||||
footer: {
|
|
||||||
// UserScripts get wrapped in an iife, so define Vencord prop on window that returns our local
|
|
||||||
js: "Object.defineProperty(unsafeWindow,'Vencord',{get:()=>Vencord});"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]
|
|
||||||
).catch(err => {
|
|
||||||
console.error("Build failed");
|
|
||||||
console.error(err.message);
|
|
||||||
if (!commonOpts.watch)
|
|
||||||
process.exit(1);
|
|
||||||
});;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {(dir: string) => Promise<string[]>}
|
* @type {(dir: string) => Promise<string[]>}
|
||||||
|
@ -155,16 +145,13 @@ async function buildExtension(target, files) {
|
||||||
const entries = {
|
const entries = {
|
||||||
"dist/Vencord.js": await readFile("dist/extension.js"),
|
"dist/Vencord.js": await readFile("dist/extension.js"),
|
||||||
"dist/Vencord.css": await readFile("dist/extension.css"),
|
"dist/Vencord.css": await readFile("dist/extension.css"),
|
||||||
...await loadDir("dist/monaco"),
|
...await loadDir("dist/vendor/monaco", "dist/"),
|
||||||
...Object.fromEntries(await Promise.all(RnNoiseFiles.map(async file =>
|
|
||||||
[`third-party/rnnoise/${file.replace(/^dist\//, "")}`, await readFile(`node_modules/@sapphi-red/web-noise-suppressor/${file}`)]
|
|
||||||
))),
|
|
||||||
...Object.fromEntries(await Promise.all(files.map(async f => {
|
...Object.fromEntries(await Promise.all(files.map(async f => {
|
||||||
let content = await readFile(join("browser", f));
|
let content = await readFile(join("browser", f));
|
||||||
if (f.startsWith("manifest")) {
|
if (f.startsWith("manifest")) {
|
||||||
const json = JSON.parse(content.toString("utf-8"));
|
const json = JSON.parse(content.toString("utf-8"));
|
||||||
json.version = VERSION;
|
json.version = VERSION;
|
||||||
content = new TextEncoder().encode(JSON.stringify(json));
|
content = Buffer.from(new TextEncoder().encode(JSON.stringify(json)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
@ -208,7 +195,6 @@ if (!process.argv.includes("--skip-extension")) {
|
||||||
|
|
||||||
Zip.sync.zip("dist/firefox-unpacked").compress().save("dist/extension-firefox.zip");
|
Zip.sync.zip("dist/firefox-unpacked").compress().save("dist/extension-firefox.zip");
|
||||||
console.info("Packed Firefox Extension written to dist/extension-firefox.zip");
|
console.info("Packed Firefox Extension written to dist/extension-firefox.zip");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
await appendCssRuntime;
|
await appendCssRuntime;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
import "../suppressExperimentalWarnings.js";
|
import "../suppressExperimentalWarnings.js";
|
||||||
import "../checkNodeVersion.js";
|
import "../checkNodeVersion.js";
|
||||||
|
|
||||||
import { exec, execSync } from "child_process";
|
import { exec, execSync } from "child_process";
|
||||||
import esbuild from "esbuild";
|
import esbuild, { build, context } from "esbuild";
|
||||||
import { constants as FsConstants, readFileSync } from "fs";
|
import { constants as FsConstants, readFileSync } from "fs";
|
||||||
import { access, readdir, readFile } from "fs/promises";
|
import { access, readdir, readFile } from "fs/promises";
|
||||||
import { minify as minifyHtml } from "html-minifier-terser";
|
import { minify as minifyHtml } from "html-minifier-terser";
|
||||||
|
@ -31,7 +33,7 @@ import { getPluginTarget } from "../utils.mjs";
|
||||||
import { builtinModules } from "module";
|
import { builtinModules } from "module";
|
||||||
|
|
||||||
/** @type {import("../../package.json")} */
|
/** @type {import("../../package.json")} */
|
||||||
const PackageJSON = JSON.parse(readFileSync("package.json"));
|
const PackageJSON = JSON.parse(readFileSync("package.json", "utf-8"));
|
||||||
|
|
||||||
export const VERSION = PackageJSON.version;
|
export const VERSION = PackageJSON.version;
|
||||||
// https://reproducible-builds.org/docs/source-date-epoch/
|
// https://reproducible-builds.org/docs/source-date-epoch/
|
||||||
|
@ -54,6 +56,34 @@ export const banner = {
|
||||||
`.trim()
|
`.trim()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON.stringify all values in an object
|
||||||
|
* @type {(obj: Record<string, any>) => Record<string, string>}
|
||||||
|
*/
|
||||||
|
export function stringifyValues(obj) {
|
||||||
|
for (const key in obj) {
|
||||||
|
obj[key] = JSON.stringify(obj[key]);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import("esbuild").BuildOptions[]} buildConfigs
|
||||||
|
*/
|
||||||
|
export async function buildOrWatchAll(buildConfigs) {
|
||||||
|
if (watch) {
|
||||||
|
await Promise.all(buildConfigs.map(cfg =>
|
||||||
|
context(cfg).then(ctx => ctx.watch())
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
await Promise.all(buildConfigs.map(cfg => build(cfg)))
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error.message);
|
||||||
|
process.exit(1); // exit immediately to skip the rest of the builds
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const PluginDefinitionNameMatcher = /definePlugin\(\{\s*(["'])?name\1:\s*(["'`])(.+?)\2/;
|
const PluginDefinitionNameMatcher = /definePlugin\(\{\s*(["'])?name\1:\s*(["'`])(.+?)\2/;
|
||||||
/**
|
/**
|
||||||
* @param {string} base
|
* @param {string} base
|
||||||
|
@ -311,18 +341,16 @@ export const banImportPlugin = (filter, message) => ({
|
||||||
export const commonOpts = {
|
export const commonOpts = {
|
||||||
logLevel: "info",
|
logLevel: "info",
|
||||||
bundle: true,
|
bundle: true,
|
||||||
watch,
|
|
||||||
minify: !watch && !IS_REPORTER,
|
minify: !watch && !IS_REPORTER,
|
||||||
sourcemap: watch ? "inline" : "",
|
sourcemap: watch ? "inline" : "external",
|
||||||
legalComments: "linked",
|
legalComments: "linked",
|
||||||
banner,
|
banner,
|
||||||
plugins: [fileUrlPlugin, gitHashPlugin, gitRemotePlugin, stylePlugin],
|
plugins: [fileUrlPlugin, gitHashPlugin, gitRemotePlugin, stylePlugin],
|
||||||
external: ["~plugins", "~git-hash", "~git-remote", "/assets/*"],
|
external: ["~plugins", "~git-hash", "~git-remote", "/assets/*"],
|
||||||
inject: ["./scripts/build/inject/react.mjs"],
|
inject: ["./scripts/build/inject/react.mjs"],
|
||||||
|
jsx: "transform",
|
||||||
jsxFactory: "VencordCreateElement",
|
jsxFactory: "VencordCreateElement",
|
||||||
jsxFragment: "VencordFragment",
|
jsxFragment: "VencordFragment"
|
||||||
// Work around https://github.com/evanw/esbuild/issues/2460
|
|
||||||
tsconfig: "./scripts/build/tsconfig.esbuild.json"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const escapedBuiltinModules = builtinModules
|
const escapedBuiltinModules = builtinModules
|
||||||
|
@ -335,5 +363,6 @@ export const commonRendererPlugins = [
|
||||||
banImportPlugin(/^react$/, "Cannot import from react. React and hooks should be imported from @webpack/common"),
|
banImportPlugin(/^react$/, "Cannot import from react. React and hooks should be imported from @webpack/common"),
|
||||||
banImportPlugin(/^electron(\/.*)?$/, "Cannot import electron in browser code. You need to use a native.ts file"),
|
banImportPlugin(/^electron(\/.*)?$/, "Cannot import electron in browser code. You need to use a native.ts file"),
|
||||||
banImportPlugin(/^ts-pattern$/, "Cannot import from ts-pattern. match and P should be imported from @webpack/common"),
|
banImportPlugin(/^ts-pattern$/, "Cannot import from ts-pattern. match and P should be imported from @webpack/common"),
|
||||||
|
// @ts-ignore this is never undefined
|
||||||
...commonOpts.plugins
|
...commonOpts.plugins
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
// Work around https://github.com/evanw/esbuild/issues/2460
|
|
||||||
{
|
|
||||||
"extends": "../../tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"jsx": "react"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,11 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable no-fallthrough */
|
|
||||||
|
|
||||||
// eslint-disable-next-line spaced-comment
|
|
||||||
/// <reference types="../src/globals" />
|
/// <reference types="../src/globals" />
|
||||||
// eslint-disable-next-line spaced-comment
|
|
||||||
/// <reference types="../src/modules" />
|
/// <reference types="../src/modules" />
|
||||||
|
|
||||||
import { createHmac } from "crypto";
|
import { createHmac } from "crypto";
|
||||||
|
@ -58,14 +54,17 @@ async function maybeGetError(handle: JSHandle): Promise<string | undefined> {
|
||||||
.catch(() => undefined);
|
.catch(() => undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface PatchInfo {
|
||||||
|
plugin: string;
|
||||||
|
type: string;
|
||||||
|
id: string;
|
||||||
|
match: string;
|
||||||
|
error?: string;
|
||||||
|
};
|
||||||
|
|
||||||
const report = {
|
const report = {
|
||||||
badPatches: [] as {
|
badPatches: [] as PatchInfo[],
|
||||||
plugin: string;
|
slowPatches: [] as PatchInfo[],
|
||||||
type: string;
|
|
||||||
id: string;
|
|
||||||
match: string;
|
|
||||||
error?: string;
|
|
||||||
}[],
|
|
||||||
badStarts: [] as {
|
badStarts: [] as {
|
||||||
plugin: string;
|
plugin: string;
|
||||||
error: string;
|
error: string;
|
||||||
|
@ -136,53 +135,67 @@ async function printReport() {
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
if (process.env.WEBHOOK_URL) {
|
if (process.env.WEBHOOK_URL) {
|
||||||
|
const patchesToEmbed = (title: string, patches: PatchInfo[], color: number) => ({
|
||||||
|
title,
|
||||||
|
color,
|
||||||
|
description: patches.map(p => {
|
||||||
|
const lines = [
|
||||||
|
`**__${p.plugin} (${p.type}):__**`,
|
||||||
|
`ID: \`${p.id}\``,
|
||||||
|
`Match: ${toCodeBlock(p.match, "Match: ".length, true)}`
|
||||||
|
];
|
||||||
|
if (p.error) lines.push(`Error: ${toCodeBlock(p.error, "Error: ".length, true)}`);
|
||||||
|
|
||||||
|
return lines.join("\n");
|
||||||
|
}).join("\n\n"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const embeds = [
|
||||||
|
{
|
||||||
|
author: {
|
||||||
|
name: `Discord ${CANARY ? "Canary" : "Stable"} (${metaData.buildNumber})`,
|
||||||
|
url: `https://nelly.tools/builds/app/${metaData.buildHash}`,
|
||||||
|
icon_url: CANARY ? "https://cdn.discordapp.com/emojis/1252721945699549327.png?size=128" : "https://cdn.discordapp.com/emojis/1252721943463985272.png?size=128"
|
||||||
|
},
|
||||||
|
color: CANARY ? 0xfbb642 : 0x5865f2
|
||||||
|
},
|
||||||
|
report.badPatches.length > 0 && patchesToEmbed("Bad Patches", report.badPatches, 0xff0000),
|
||||||
|
report.slowPatches.length > 0 && patchesToEmbed("Slow Patches", report.slowPatches, 0xf0b232),
|
||||||
|
report.badWebpackFinds.length > 0 && {
|
||||||
|
title: "Bad Webpack Finds",
|
||||||
|
description: report.badWebpackFinds.map(f => toCodeBlock(f, 0, true)).join("\n") || "None",
|
||||||
|
color: 0xff0000
|
||||||
|
},
|
||||||
|
report.badStarts.length > 0 && {
|
||||||
|
title: "Bad Starts",
|
||||||
|
description: report.badStarts.map(p => {
|
||||||
|
const lines = [
|
||||||
|
`**__${p.plugin}:__**`,
|
||||||
|
toCodeBlock(p.error, 0, true)
|
||||||
|
];
|
||||||
|
return lines.join("\n");
|
||||||
|
}
|
||||||
|
).join("\n\n") || "None",
|
||||||
|
color: 0xff0000
|
||||||
|
},
|
||||||
|
report.otherErrors.length > 0 && {
|
||||||
|
title: "Discord Errors",
|
||||||
|
description: report.otherErrors.length ? toCodeBlock(report.otherErrors.join("\n"), 0, true) : "None",
|
||||||
|
color: 0xff0000
|
||||||
|
}
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
|
if (embeds.length === 1) {
|
||||||
|
embeds.push({
|
||||||
|
title: "No issues found",
|
||||||
|
description: "Seems like everything is working fine (for now) <:shipit:1330992641466433556>",
|
||||||
|
color: 0x00ff00
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const body = JSON.stringify({
|
const body = JSON.stringify({
|
||||||
username: "Vencord Reporter" + (CANARY ? " (Canary)" : ""),
|
username: "Vencord Reporter" + (CANARY ? " (Canary)" : ""),
|
||||||
embeds: [
|
embeds
|
||||||
{
|
|
||||||
author: {
|
|
||||||
name: `Discord ${CANARY ? "Canary" : "Stable"} (${metaData.buildNumber})`,
|
|
||||||
url: `https://nelly.tools/builds/app/${metaData.buildHash}`,
|
|
||||||
icon_url: CANARY ? "https://cdn.discordapp.com/emojis/1252721945699549327.png?size=128" : "https://cdn.discordapp.com/emojis/1252721943463985272.png?size=128"
|
|
||||||
},
|
|
||||||
color: CANARY ? 0xfbb642 : 0x5865f2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Bad Patches",
|
|
||||||
description: report.badPatches.map(p => {
|
|
||||||
const lines = [
|
|
||||||
`**__${p.plugin} (${p.type}):__**`,
|
|
||||||
`ID: \`${p.id}\``,
|
|
||||||
`Match: ${toCodeBlock(p.match, "Match: ".length, true)}`
|
|
||||||
];
|
|
||||||
if (p.error) lines.push(`Error: ${toCodeBlock(p.error, "Error: ".length, true)}`);
|
|
||||||
return lines.join("\n");
|
|
||||||
}).join("\n\n") || "None",
|
|
||||||
color: report.badPatches.length ? 0xff0000 : 0x00ff00
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Bad Webpack Finds",
|
|
||||||
description: report.badWebpackFinds.map(f => toCodeBlock(f, 0, true)).join("\n") || "None",
|
|
||||||
color: report.badWebpackFinds.length ? 0xff0000 : 0x00ff00
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Bad Starts",
|
|
||||||
description: report.badStarts.map(p => {
|
|
||||||
const lines = [
|
|
||||||
`**__${p.plugin}:__**`,
|
|
||||||
toCodeBlock(p.error, 0, true)
|
|
||||||
];
|
|
||||||
return lines.join("\n");
|
|
||||||
}
|
|
||||||
).join("\n\n") || "None",
|
|
||||||
color: report.badStarts.length ? 0xff0000 : 0x00ff00
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Discord Errors",
|
|
||||||
description: report.otherErrors.length ? toCodeBlock(report.otherErrors.join("\n"), 0, true) : "None",
|
|
||||||
color: report.otherErrors.length ? 0xff0000 : 0x00ff00
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
|
@ -249,14 +262,17 @@ page.on("console", async e => {
|
||||||
|
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case "WebpackInterceptor:":
|
case "WebpackInterceptor:":
|
||||||
const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module|took [\d.]+?ms) \(Module id is (.+?)\): (.+)/)!;
|
const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/);
|
||||||
if (!patchFailMatch) break;
|
const patchSlowMatch = message.match(/Patch by (.+?) (took [\d.]+?ms) \(Module id is (.+?)\): (.+)/);
|
||||||
|
const match = patchFailMatch ?? patchSlowMatch;
|
||||||
|
if (!match) break;
|
||||||
|
|
||||||
logStderr(await getText());
|
logStderr(await getText());
|
||||||
process.exitCode = 1;
|
process.exitCode = 1;
|
||||||
|
|
||||||
const [, plugin, type, id, regex] = patchFailMatch;
|
const [, plugin, type, id, regex] = match;
|
||||||
report.badPatches.push({
|
const list = patchFailMatch ? report.badPatches : report.slowPatches;
|
||||||
|
list.push({
|
||||||
plugin,
|
plugin,
|
||||||
type,
|
type,
|
||||||
id,
|
id,
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { Settings } from "@api/Settings";
|
||||||
import { PluginIpcMappings } from "@main/ipcPlugins";
|
import { PluginIpcMappings } from "@main/ipcPlugins";
|
||||||
import type { UserThemeHeader } from "@main/themes";
|
import type { UserThemeHeader } from "@main/themes";
|
||||||
import { IpcEvents } from "@shared/IpcEvents";
|
import { IpcEvents } from "@shared/IpcEvents";
|
||||||
import { IpcRes } from "@utils/types";
|
import { IpcRes } from "@utils/types";
|
||||||
import type { Settings } from "api/Settings";
|
|
||||||
import { ipcRenderer } from "electron";
|
import { ipcRenderer } from "electron";
|
||||||
|
|
||||||
function invoke<T = any>(event: IpcEvents, ...args: any[]) {
|
function invoke<T = any>(event: IpcEvents, ...args: any[]) {
|
||||||
|
|
|
@ -65,7 +65,7 @@ function ReplacementComponent({ module, match, replacement, setReplacementError
|
||||||
}
|
}
|
||||||
const canonicalMatch = canonicalizeMatch(new RegExp(match));
|
const canonicalMatch = canonicalizeMatch(new RegExp(match));
|
||||||
try {
|
try {
|
||||||
const canonicalReplace = canonicalizeReplace(replacement, "YourPlugin");
|
const canonicalReplace = canonicalizeReplace(replacement, 'Vencord.Plugins.plugins["YourPlugin"]');
|
||||||
var patched = src.replace(canonicalMatch, canonicalReplace as string);
|
var patched = src.replace(canonicalMatch, canonicalReplace as string);
|
||||||
setReplacementError(void 0);
|
setReplacementError(void 0);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { Logger } from "@utils/Logger";
|
||||||
import { canonicalizeMatch } from "@utils/patches";
|
import { canonicalizeMatch } from "@utils/patches";
|
||||||
import * as Webpack from "@webpack";
|
import * as Webpack from "@webpack";
|
||||||
import { wreq } from "@webpack";
|
import { wreq } from "@webpack";
|
||||||
import { AnyModuleFactory, ModuleFactory } from "webpack";
|
import { AnyModuleFactory, ModuleFactory } from "@webpack/wreq.d";
|
||||||
|
|
||||||
export async function loadLazyChunks() {
|
export async function loadLazyChunks() {
|
||||||
const LazyChunkLoaderLogger = new Logger("LazyChunkLoader");
|
const LazyChunkLoaderLogger = new Logger("LazyChunkLoader");
|
||||||
|
@ -20,8 +20,7 @@ export async function loadLazyChunks() {
|
||||||
const invalidChunks = new Set<PropertyKey>();
|
const invalidChunks = new Set<PropertyKey>();
|
||||||
const deferredRequires = new Set<PropertyKey>();
|
const deferredRequires = new Set<PropertyKey>();
|
||||||
|
|
||||||
let chunksSearchingResolve: (value: void) => void;
|
const { promise: chunksSearchingDone, resolve: chunksSearchingResolve } = Promise.withResolvers<void>();
|
||||||
const chunksSearchingDone = new Promise<void>(r => chunksSearchingResolve = r);
|
|
||||||
|
|
||||||
// True if resolved, false otherwise
|
// True if resolved, false otherwise
|
||||||
const chunksSearchPromises = [] as Array<() => boolean>;
|
const chunksSearchPromises = [] as Array<() => boolean>;
|
||||||
|
@ -69,7 +68,7 @@ export async function loadLazyChunks() {
|
||||||
|
|
||||||
const isWorkerAsset = await fetch(wreq.p + wreq.u(id))
|
const isWorkerAsset = await fetch(wreq.p + wreq.u(id))
|
||||||
.then(r => r.text())
|
.then(r => r.text())
|
||||||
.then(t => t.includes("importScripts("));
|
.then(t => /importScripts\(|self\.postMessage/.test(t));
|
||||||
|
|
||||||
if (isWorkerAsset) {
|
if (isWorkerAsset) {
|
||||||
invalidChunks.add(id);
|
invalidChunks.add(id);
|
||||||
|
@ -140,8 +139,8 @@ export async function loadLazyChunks() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Webpack.factoryListeners.add(factoryListener);
|
Webpack.factoryListeners.add(factoryListener);
|
||||||
for (const factoryId in wreq.m) {
|
for (const moduleId in wreq.m) {
|
||||||
factoryListener(wreq.m[factoryId]);
|
factoryListener(wreq.m[moduleId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
await chunksSearchingDone;
|
await chunksSearchingDone;
|
||||||
|
@ -175,7 +174,7 @@ export async function loadLazyChunks() {
|
||||||
await Promise.all(chunksLeft.map(async id => {
|
await Promise.all(chunksLeft.map(async id => {
|
||||||
const isWorkerAsset = await fetch(wreq.p + wreq.u(id))
|
const isWorkerAsset = await fetch(wreq.p + wreq.u(id))
|
||||||
.then(r => r.text())
|
.then(r => r.text())
|
||||||
.then(t => t.includes("importScripts("));
|
.then(t => /importScripts\(|self\.postMessage/.test(t));
|
||||||
|
|
||||||
// Loads the chunk. Currently this only happens with the language packs which are loaded differently
|
// Loads the chunk. Currently this only happens with the language packs which are loaded differently
|
||||||
if (!isWorkerAsset) {
|
if (!isWorkerAsset) {
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
|
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import * as Webpack from "@webpack";
|
import * as Webpack from "@webpack";
|
||||||
import { addPatch, patches } from "plugins";
|
import { getBuildNumber, patchTimings } from "@webpack/patcher";
|
||||||
import { getBuildNumber } from "webpack/patchWebpack";
|
|
||||||
|
|
||||||
|
import { addPatch, patches } from "../plugins";
|
||||||
import { loadLazyChunks } from "./loadLazyChunks";
|
import { loadLazyChunks } from "./loadLazyChunks";
|
||||||
|
|
||||||
async function runReporter() {
|
async function runReporter() {
|
||||||
|
@ -17,8 +17,7 @@ async function runReporter() {
|
||||||
try {
|
try {
|
||||||
ReporterLogger.log("Starting test...");
|
ReporterLogger.log("Starting test...");
|
||||||
|
|
||||||
let loadLazyChunksResolve: (value: void) => void;
|
const { promise: loadLazyChunksDone, resolve: loadLazyChunksResolve } = Promise.withResolvers<void>();
|
||||||
const loadLazyChunksDone = new Promise<void>(r => loadLazyChunksResolve = r);
|
|
||||||
|
|
||||||
// The main patch for starting the reporter chunk loading
|
// The main patch for starting the reporter chunk loading
|
||||||
addPatch({
|
addPatch({
|
||||||
|
@ -51,7 +50,7 @@ async function runReporter() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [plugin, moduleId, match, totalTime] of Vencord.WebpackPatcher.patchTimings) {
|
for (const [plugin, moduleId, match, totalTime] of patchTimings) {
|
||||||
if (totalTime > 5) {
|
if (totalTime > 5) {
|
||||||
new Logger("WebpackInterceptor").warn(`Patch by ${plugin} took ${Math.round(totalTime * 100) / 100}ms (Module id is ${String(moduleId)}): ${match}`);
|
new Logger("WebpackInterceptor").warn(`Patch by ${plugin} took ${Math.round(totalTime * 100) / 100}ms (Module id is ${String(moduleId)}): ${match}`);
|
||||||
}
|
}
|
||||||
|
@ -79,9 +78,9 @@ async function runReporter() {
|
||||||
result = await Webpack.extractAndLoadChunks(code, matcher);
|
result = await Webpack.extractAndLoadChunks(code, matcher);
|
||||||
if (result === false) result = null;
|
if (result === false) result = null;
|
||||||
} else if (method === "mapMangledModule") {
|
} else if (method === "mapMangledModule") {
|
||||||
const [code, mapper] = args;
|
const [code, mapper, includeBlacklistedExports] = args;
|
||||||
|
|
||||||
result = Webpack.mapMangledModule(code, mapper);
|
result = Webpack.mapMangledModule(code, mapper, includeBlacklistedExports);
|
||||||
if (Object.keys(result).length !== Object.keys(mapper).length) throw new Error("Webpack Find Fail");
|
if (Object.keys(result).length !== Object.keys(mapper).length) throw new Error("Webpack Find Fail");
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
|
@ -16,8 +16,8 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: "SCALE_DOWN:",
|
find: "SCALE_DOWN:",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /!\(null==(\i)\|\|0===\i\|\|null==(\i)\|\|0===\i\)/,
|
match: /(?<="IMAGE"===\i\?)\i(?=\?)/,
|
||||||
replace: (_, width, height) => `!((null == ${width} || 0 === ${width}) && (null == ${height} || 0 === ${height}))`
|
replace: "true"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import definePlugin, { OptionType, StartAt } from "@utils/types";
|
import definePlugin, { OptionType, StartAt } from "@utils/types";
|
||||||
import { WebpackRequire } from "webpack";
|
import { WebpackRequire } from "@webpack/wreq.d";
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
disableAnalytics: {
|
disableAnalytics: {
|
||||||
|
|
|
@ -158,6 +158,9 @@ export default definePlugin({
|
||||||
aboveActivity: getIntlMessage("ACTIVITY_SETTINGS")
|
aboveActivity: getIntlMessage("ACTIVITY_SETTINGS")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!names[settingsLocation] || names[settingsLocation].endsWith("_SETTINGS"))
|
||||||
|
return firstChild === "PREMIUM";
|
||||||
|
|
||||||
return header === names[settingsLocation];
|
return header === names[settingsLocation];
|
||||||
} catch {
|
} catch {
|
||||||
return firstChild === "PREMIUM";
|
return firstChild === "PREMIUM";
|
||||||
|
|
|
@ -85,7 +85,7 @@ export default definePlugin({
|
||||||
replace: "$&onRequestClose:$self.onPopoutClose,"
|
replace: "$&onRequestClose:$self.onPopoutClose,"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /(?<=\.avatarWrapper,)/,
|
match: /(?<=#{intl::SET_STATUS}\),)/,
|
||||||
replace: "ref:$self.accountPanelRef,onContextMenu:$self.openAccountPanelContextMenu,"
|
replace: "ref:$self.accountPanelRef,onContextMenu:$self.openAccountPanelContextMenu,"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -185,8 +185,8 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
// Decide if we should render the expanded folder background if we are rendering the Better Folders sidebar
|
// Decide if we should render the expanded folder background if we are rendering the Better Folders sidebar
|
||||||
predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always,
|
predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always,
|
||||||
match: /(?<=\.isExpanded\),children:\[)/,
|
match: /\.isExpanded\),.{0,30}children:\[/,
|
||||||
replace: "$self.shouldShowFolderIconAndBackground(!!arguments[0]?.isBetterFolders,arguments[0]?.betterFoldersExpandedIds)&&"
|
replace: "$&$self.shouldShowFolderIconAndBackground(!!arguments[0]?.isBetterFolders,arguments[0]?.betterFoldersExpandedIds)&&"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Decide if we should render the expanded folder icon if we are rendering the Better Folders sidebar
|
// Decide if we should render the expanded folder icon if we are rendering the Better Folders sidebar
|
||||||
|
|
|
@ -26,10 +26,18 @@ export default definePlugin({
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: '"ChannelAttachButton"',
|
find: '"ChannelAttachButton"',
|
||||||
replacement: {
|
replacement: [
|
||||||
match: /\.attachButtonInner,"aria-label":.{0,50},onDoubleClick:(.+?:void 0),.{0,30}?\.\.\.(\i),/,
|
{
|
||||||
replace: "$&onClick:$1,onContextMenu:$2.onClick,",
|
// FIXME(Bundler spread transform related): Remove old compatiblity once enough time has passed, if they don't revert
|
||||||
},
|
match: /\.attachButtonInner,"aria-label":.{0,50},onDoubleClick:(.+?:void 0),.{0,30}?\.\.\.(\i),/,
|
||||||
|
replace: "$&onClick:$1,onContextMenu:$2.onClick,",
|
||||||
|
noWarn: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /\.attachButtonInner,"aria-label":.{0,50},onDoubleClick:(.+?:void 0),.{0,100}\},(\i)\).{0,100}children:\i/,
|
||||||
|
replace: "$&,onClick:$1,onContextMenu:$2.onClick,",
|
||||||
|
},
|
||||||
|
]
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
|
@ -50,12 +50,24 @@ export default definePlugin({
|
||||||
find: ".decorationGridItem,",
|
find: ".decorationGridItem,",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(?<==)\i=>{let{children.{20,100}decorationGridItem/,
|
// FIXME(Bundler spread transform related): Remove old compatiblity once enough time has passed, if they don't revert
|
||||||
replace: "$self.DecorationGridItem=$&"
|
match: /(?<==)\i=>{let{children.{20,200}decorationGridItem/,
|
||||||
|
replace: "$self.DecorationGridItem=$&",
|
||||||
|
noWarn: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// FIXME(Bundler spread transform related): Remove old compatiblity once enough time has passed, if they don't revert
|
||||||
match: /(?<==)\i=>{let{user:\i,avatarDecoration/,
|
match: /(?<==)\i=>{let{user:\i,avatarDecoration/,
|
||||||
replace: "$self.DecorationGridDecoration=$&"
|
replace: "$self.DecorationGridDecoration=$&",
|
||||||
|
noWarn: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /(?<==)\i=>{var{children.{20,200}decorationGridItem/,
|
||||||
|
replace: "$self.DecorationGridItem=$&",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /(?<==)\i=>{var{user:\i,avatarDecoration/,
|
||||||
|
replace: "$self.DecorationGridDecoration=$&",
|
||||||
},
|
},
|
||||||
// Remove NEW label from decor avatar decorations
|
// Remove NEW label from decor avatar decorations
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CopyIcon, DeleteIcon } from "@components/Icons";
|
import { CopyIcon, DeleteIcon } from "@components/Icons";
|
||||||
import { Alerts, Clipboard, ContextMenuApi, Menu, UserStore } from "webpack/common";
|
import { Alerts, Clipboard, ContextMenuApi, Menu, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
import { Decoration } from "../../lib/api";
|
import { Decoration } from "../../lib/api";
|
||||||
import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDecorationsStore";
|
import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDecorationsStore";
|
||||||
|
|
|
@ -173,7 +173,7 @@ function initWs(isManual = false) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const matcher = canonicalizeMatch(parseNode(match));
|
const matcher = canonicalizeMatch(parseNode(match));
|
||||||
const replacement = canonicalizeReplace(parseNode(replace), "PlaceHolderPluginName");
|
const replacement = canonicalizeReplace(parseNode(replace), 'Vencord.Plugins.plugins["PlaceHolderPluginName"]');
|
||||||
|
|
||||||
const newSource = src.replace(matcher, replacement as string);
|
const newSource = src.replace(matcher, replacement as string);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { app } from "electron";
|
||||||
|
|
||||||
app.on("browser-window-created", (_, win) => {
|
app.on("browser-window-created", (_, win) => {
|
||||||
win.webContents.on("frame-created", (_, { frame }) => {
|
win.webContents.on("frame-created", (_, { frame }) => {
|
||||||
frame.once("dom-ready", () => {
|
frame?.once("dom-ready", () => {
|
||||||
if (frame.url.startsWith("https://open.spotify.com/embed/")) {
|
if (frame.url.startsWith("https://open.spotify.com/embed/")) {
|
||||||
const settings = RendererSettings.store.plugins?.FixSpotifyEmbeds;
|
const settings = RendererSettings.store.plugins?.FixSpotifyEmbeds;
|
||||||
if (!settings?.enabled) return;
|
if (!settings?.enabled) return;
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { app } from "electron";
|
||||||
|
|
||||||
app.on("browser-window-created", (_, win) => {
|
app.on("browser-window-created", (_, win) => {
|
||||||
win.webContents.on("frame-created", (_, { frame }) => {
|
win.webContents.on("frame-created", (_, { frame }) => {
|
||||||
frame.once("dom-ready", () => {
|
frame?.once("dom-ready", () => {
|
||||||
if (frame.url.startsWith("https://www.youtube.com/")) {
|
if (frame.url.startsWith("https://www.youtube.com/")) {
|
||||||
const settings = RendererSettings.store.plugins?.FixYoutubeEmbeds;
|
const settings = RendererSettings.store.plugins?.FixYoutubeEmbeds;
|
||||||
if (!settings?.enabled) return;
|
if (!settings?.enabled) return;
|
||||||
|
|
|
@ -31,7 +31,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
name: "create friend invite",
|
name: "create friend invite",
|
||||||
description: "Generates a friend invite link.",
|
description: "Generates a friend invite link.",
|
||||||
inputType: ApplicationCommandInputType.BOT,
|
inputType: ApplicationCommandInputType.BUILT_IN,
|
||||||
|
|
||||||
execute: async (args, ctx) => {
|
execute: async (args, ctx) => {
|
||||||
const invite = await FriendInvites.createFriendInvite();
|
const invite = await FriendInvites.createFriendInvite();
|
||||||
|
@ -48,7 +48,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
name: "view friend invites",
|
name: "view friend invites",
|
||||||
description: "View a list of all generated friend invites.",
|
description: "View a list of all generated friend invites.",
|
||||||
inputType: ApplicationCommandInputType.BOT,
|
inputType: ApplicationCommandInputType.BUILT_IN,
|
||||||
execute: async (_, ctx) => {
|
execute: async (_, ctx) => {
|
||||||
const invites = await FriendInvites.getAllFriendInvites();
|
const invites = await FriendInvites.getAllFriendInvites();
|
||||||
const friendInviteList = invites.map(i =>
|
const friendInviteList = invites.map(i =>
|
||||||
|
@ -67,7 +67,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
name: "revoke friend invites",
|
name: "revoke friend invites",
|
||||||
description: "Revokes all generated friend invites.",
|
description: "Revokes all generated friend invites.",
|
||||||
inputType: ApplicationCommandInputType.BOT,
|
inputType: ApplicationCommandInputType.BUILT_IN,
|
||||||
execute: async (_, ctx) => {
|
execute: async (_, ctx) => {
|
||||||
await FriendInvites.revokeFriendInvites();
|
await FriendInvites.revokeFriendInvites();
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { Devs } from "@utils/constants";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findStoreLazy } from "@webpack";
|
import { findStoreLazy } from "@webpack";
|
||||||
import { Button, Forms, showToast, TextInput, Toasts, Tooltip, useEffect, useState } from "webpack/common";
|
import { Button, Forms, showToast, TextInput, Toasts, Tooltip, useEffect, useState } from "@webpack/common";
|
||||||
|
|
||||||
const enum ActivitiesTypes {
|
const enum ActivitiesTypes {
|
||||||
Game,
|
Game,
|
||||||
|
|
|
@ -34,7 +34,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: "#{intl::FRIENDS_ALL_HEADER}",
|
find: "#{intl::FRIENDS_ALL_HEADER}",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /toString\(\)\}\);case (\i\.\i)\.BLOCKED/,
|
match: /toString\(\)\}\);case (\i\.\i)\.PENDING/,
|
||||||
replace: 'toString()});case $1.IMPLICIT:return "Implicit — "+arguments[1];case $1.BLOCKED'
|
replace: 'toString()});case $1.IMPLICIT:return "Implicit — "+arguments[1];case $1.BLOCKED'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -50,7 +50,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: "#{intl::FRIENDS_SECTION_ONLINE}",
|
find: "#{intl::FRIENDS_SECTION_ONLINE}",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /,{id:(\i\.\i)\.BLOCKED,show:.+?className:(\i\.item)/,
|
match: /,{id:(\i\.\i)\.PENDING,show:.+?className:(\i\.item)/,
|
||||||
replace: (rest, relationShipTypes, className) => `,{id:${relationShipTypes}.IMPLICIT,show:true,className:${className},content:"Implicit"}${rest}`
|
replace: (rest, relationShipTypes, className) => `,{id:${relationShipTypes}.IMPLICIT,show:true,className:${className},content:"Implicit"}${rest}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -58,7 +58,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: '"FriendsStore"',
|
find: '"FriendsStore"',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=case (\i\.\i)\.BLOCKED:return (\i)\.type===\i\.\i\.BLOCKED)/,
|
match: /(?<=case (\i\.\i)\.SUGGESTIONS:return \d+===(\i)\.type)/,
|
||||||
replace: ";case $1.IMPLICIT:return $2.type===5"
|
replace: ";case $1.IMPLICIT:return $2.type===5"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -31,6 +31,7 @@ import { Logger } from "@utils/Logger";
|
||||||
import { canonicalizeFind, canonicalizeReplacement } from "@utils/patches";
|
import { canonicalizeFind, canonicalizeReplacement } from "@utils/patches";
|
||||||
import { Patch, Plugin, PluginDef, ReporterTestable, StartAt } from "@utils/types";
|
import { Patch, Plugin, PluginDef, ReporterTestable, StartAt } from "@utils/types";
|
||||||
import { FluxDispatcher } from "@webpack/common";
|
import { FluxDispatcher } from "@webpack/common";
|
||||||
|
import { patches } from "@webpack/patcher";
|
||||||
import { FluxEvents } from "@webpack/types";
|
import { FluxEvents } from "@webpack/types";
|
||||||
|
|
||||||
import Plugins from "~plugins";
|
import Plugins from "~plugins";
|
||||||
|
@ -41,7 +42,7 @@ const logger = new Logger("PluginManager", "#a6d189");
|
||||||
|
|
||||||
export const PMLogger = logger;
|
export const PMLogger = logger;
|
||||||
export const plugins = Plugins;
|
export const plugins = Plugins;
|
||||||
export const patches = [] as Patch[];
|
export { patches };
|
||||||
|
|
||||||
/** Whether we have subscribed to flux events of all the enabled plugins when FluxDispatcher was ready */
|
/** Whether we have subscribed to flux events of all the enabled plugins when FluxDispatcher was ready */
|
||||||
let enabledPluginsSubscribedFlux = false;
|
let enabledPluginsSubscribedFlux = false;
|
||||||
|
|
|
@ -65,10 +65,18 @@ export default definePlugin({
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "{isSidebarVisible:",
|
find: "{isSidebarVisible:",
|
||||||
replacement: {
|
replacement: [
|
||||||
match: /(?<=let\{className:(\i),.+?children):\[(\i\.useMemo[^}]+"aria-multiselectable")/,
|
{
|
||||||
replace: ":[$1?.startsWith('members')?$self.render():null,$2"
|
// FIXME(Bundler spread transform related): Remove old compatiblity once enough time has passed, if they don't revert
|
||||||
},
|
match: /(?<=let\{className:(\i),.+?children):\[(\i\.useMemo[^}]+"aria-multiselectable")/,
|
||||||
|
replace: ":[$1?.startsWith('members')?$self.render():null,$2",
|
||||||
|
noWarn: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /(?<=var\{className:(\i),.+?children):\[(\i\.useMemo[^}]+"aria-multiselectable")/,
|
||||||
|
replace: ":[$1?.startsWith('members')?$self.render():null,$2",
|
||||||
|
},
|
||||||
|
],
|
||||||
predicate: () => settings.store.memberList
|
predicate: () => settings.store.memberList
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -84,8 +84,14 @@ export default definePlugin({
|
||||||
find: ".USER_MENTION)",
|
find: ".USER_MENTION)",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
|
// FIXME(Bundler spread transform related): Remove old compatiblity once enough time has passed, if they don't revert
|
||||||
match: /onContextMenu:\i,color:\i,\.\.\.\i(?=,children:)(?<=user:(\i),channel:(\i).{0,500}?)/,
|
match: /onContextMenu:\i,color:\i,\.\.\.\i(?=,children:)(?<=user:(\i),channel:(\i).{0,500}?)/,
|
||||||
replace: "$&,color:$self.getColorInt($1?.id,$2?.id)"
|
replace: "$&,color:$self.getColorInt($1?.id,$2?.id)",
|
||||||
|
noWarn: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /(?<=onContextMenu:\i,color:)\i(?=\},\i\),\{children)(?<=user:(\i),channel:(\i).{0,500}?)/,
|
||||||
|
replace: "$self.getColorInt($1?.id,$2?.id)",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
predicate: () => settings.store.chatMentions
|
predicate: () => settings.store.chatMentions
|
||||||
|
|
|
@ -96,6 +96,6 @@
|
||||||
|
|
||||||
.vc-shiki-root .vc-shiki-table-cell:last-child {
|
.vc-shiki-root .vc-shiki-table-cell:last-child {
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
word-break: break-word;
|
overflow-wrap: break-word;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,7 @@ export default definePlugin({
|
||||||
replacement: [
|
replacement: [
|
||||||
// Make the channel appear as muted if it's hidden
|
// Make the channel appear as muted if it's hidden
|
||||||
{
|
{
|
||||||
match: /{channel:(\i),name:\i,muted:(\i).+?;/,
|
match: /\.subtitle,.+?;(?=return\(0,\i\.jsxs?\))(?<={channel:(\i),name:\i,muted:(\i).+?;)/,
|
||||||
replace: (m, channel, muted) => `${m}${muted}=$self.isHiddenChannel(${channel})?true:${muted};`
|
replace: (m, channel, muted) => `${m}${muted}=$self.isHiddenChannel(${channel})?true:${muted};`
|
||||||
},
|
},
|
||||||
// Add the hidden eye icon if the channel is hidden
|
// Add the hidden eye icon if the channel is hidden
|
||||||
|
@ -204,7 +204,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
// Hide unreads
|
// Hide unreads
|
||||||
predicate: () => settings.store.hideUnreads === true,
|
predicate: () => settings.store.hideUnreads === true,
|
||||||
match: /{channel:(\i),name:\i,.+?unread:(\i).+?;/,
|
match: /\.subtitle,.+?;(?=return\(0,\i\.jsxs?\))(?<={channel:(\i),name:\i,.+?unread:(\i).+?)/,
|
||||||
replace: (m, channel, unread) => `${m}${unread}=$self.isHiddenChannel(${channel})?false:${unread};`
|
replace: (m, channel, unread) => `${m}${unread}=$self.isHiddenChannel(${channel})?false:${unread};`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -485,7 +485,7 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: '="NowPlayingViewStore",',
|
find: '"NowPlayingViewStore"',
|
||||||
replacement: {
|
replacement: {
|
||||||
// Make active now voice states on hidden channels
|
// Make active now voice states on hidden channels
|
||||||
match: /(getVoiceStateForUser.{0,150}?)&&\i\.\i\.canWithPartialContext.{0,20}VIEW_CHANNEL.+?}\)(?=\?)/,
|
match: /(getVoiceStateForUser.{0,150}?)&&\i\.\i\.canWithPartialContext.{0,20}VIEW_CHANNEL.+?}\)(?=\?)/,
|
||||||
|
|
|
@ -77,7 +77,7 @@ export const SpotifyStore = proxyLazyWebpack(() => {
|
||||||
|
|
||||||
class SpotifyStore extends Store {
|
class SpotifyStore extends Store {
|
||||||
public mPosition = 0;
|
public mPosition = 0;
|
||||||
private start = 0;
|
public _start = 0;
|
||||||
|
|
||||||
public track: Track | null = null;
|
public track: Track | null = null;
|
||||||
public device: Device | null = null;
|
public device: Device | null = null;
|
||||||
|
@ -100,26 +100,26 @@ export const SpotifyStore = proxyLazyWebpack(() => {
|
||||||
public get position(): number {
|
public get position(): number {
|
||||||
let pos = this.mPosition;
|
let pos = this.mPosition;
|
||||||
if (this.isPlaying) {
|
if (this.isPlaying) {
|
||||||
pos += Date.now() - this.start;
|
pos += Date.now() - this._start;
|
||||||
}
|
}
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set position(p: number) {
|
public set position(p: number) {
|
||||||
this.mPosition = p;
|
this.mPosition = p;
|
||||||
this.start = Date.now();
|
this._start = Date.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
prev() {
|
prev() {
|
||||||
this.req("post", "/previous");
|
this._req("post", "/previous");
|
||||||
}
|
}
|
||||||
|
|
||||||
next() {
|
next() {
|
||||||
this.req("post", "/next");
|
this._req("post", "/next");
|
||||||
}
|
}
|
||||||
|
|
||||||
setVolume(percent: number) {
|
setVolume(percent: number) {
|
||||||
this.req("put", "/volume", {
|
this._req("put", "/volume", {
|
||||||
query: {
|
query: {
|
||||||
volume_percent: Math.round(percent)
|
volume_percent: Math.round(percent)
|
||||||
}
|
}
|
||||||
|
@ -131,17 +131,17 @@ export const SpotifyStore = proxyLazyWebpack(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
setPlaying(playing: boolean) {
|
setPlaying(playing: boolean) {
|
||||||
this.req("put", playing ? "/play" : "/pause");
|
this._req("put", playing ? "/play" : "/pause");
|
||||||
}
|
}
|
||||||
|
|
||||||
setRepeat(state: Repeat) {
|
setRepeat(state: Repeat) {
|
||||||
this.req("put", "/repeat", {
|
this._req("put", "/repeat", {
|
||||||
query: { state }
|
query: { state }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setShuffle(state: boolean) {
|
setShuffle(state: boolean) {
|
||||||
this.req("put", "/shuffle", {
|
this._req("put", "/shuffle", {
|
||||||
query: { state }
|
query: { state }
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.shuffle = state;
|
this.shuffle = state;
|
||||||
|
@ -154,7 +154,7 @@ export const SpotifyStore = proxyLazyWebpack(() => {
|
||||||
|
|
||||||
this.isSettingPosition = true;
|
this.isSettingPosition = true;
|
||||||
|
|
||||||
return this.req("put", "/seek", {
|
return this._req("put", "/seek", {
|
||||||
query: {
|
query: {
|
||||||
position_ms: Math.round(ms)
|
position_ms: Math.round(ms)
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ export const SpotifyStore = proxyLazyWebpack(() => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private req(method: "post" | "get" | "put", route: string, data: any = {}) {
|
_req(method: "post" | "get" | "put", route: string, data: any = {}) {
|
||||||
if (this.device?.is_active)
|
if (this.device?.is_active)
|
||||||
(data.query ??= {}).device_id = this.device.id;
|
(data.query ??= {}).device_id = this.device.id;
|
||||||
|
|
||||||
|
|
|
@ -27,12 +27,22 @@ export default definePlugin({
|
||||||
authors: [Devs.Megu],
|
authors: [Devs.Megu],
|
||||||
patches: [{
|
patches: [{
|
||||||
find: "#{intl::ACTIVITY_SETTINGS}",
|
find: "#{intl::ACTIVITY_SETTINGS}",
|
||||||
replacement: {
|
replacement: [
|
||||||
match: /(?<=}\)([,;])(\i\.settings)\.forEach.+?(\i)\.push.+}\)}\))/,
|
{
|
||||||
replace: (_, commaOrSemi, settings, elements) => "" +
|
// FIXME(Bundler spread transform related): Remove old compatiblity once enough time has passed, if they don't revert
|
||||||
`${commaOrSemi}${settings}?.[0]==="CHANGELOG"` +
|
match: /(?<=}\)([,;])(\i\.settings)\.forEach.+?(\i)\.push.+}\)}\))/,
|
||||||
`&&${elements}.push({section:"StartupTimings",label:"Startup Timings",element:$self.StartupTimingPage})`
|
replace: (_, commaOrSemi, settings, elements) => "" +
|
||||||
}
|
`${commaOrSemi}${settings}?.[0]==="CHANGELOG"` +
|
||||||
|
`&&${elements}.push({section:"StartupTimings",label:"Startup Timings",element:$self.StartupTimingPage})`,
|
||||||
|
noWarn: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /(?<=}\)([,;])(\i\.settings)\.forEach.+?(\i)\.push.+\)\)\}\))(?=\)\})/,
|
||||||
|
replace: (_, commaOrSemi, settings, elements) => "" +
|
||||||
|
`${commaOrSemi}${settings}?.[0]==="CHANGELOG"` +
|
||||||
|
`&&${elements}.push({section:"StartupTimings",label:"Startup Timings",element:$self.StartupTimingPage})`,
|
||||||
|
},
|
||||||
|
]
|
||||||
}],
|
}],
|
||||||
StartupTimingPage
|
StartupTimingPage
|
||||||
});
|
});
|
||||||
|
|
|
@ -41,11 +41,20 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: '="SYSTEM_TAG"',
|
find: '="SYSTEM_TAG"',
|
||||||
replacement: {
|
replacement: [
|
||||||
// Add next to username (compact mode)
|
{
|
||||||
match: /className:\i\(\)\(\i\.className(?:,\i\.clickable)?,\i\)}\),(?=\i)/g,
|
// Add next to username (compact mode)
|
||||||
replace: "$&$self.CompactPronounsChatComponentWrapper(arguments[0]),"
|
// FIXME(Bundler spread transform related): Remove old compatiblity once enough time has passed, if they don't revert
|
||||||
}
|
match: /className:\i\(\)\(\i\.className(?:,\i\.clickable)?,\i\)}\),(?=\i)/g,
|
||||||
|
replace: "$&$self.CompactPronounsChatComponentWrapper(arguments[0]),",
|
||||||
|
noWarn: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Add next to username (compact mode)
|
||||||
|
match: /className:\i\(\)\(\i\.className(?:,\i\.clickable)?,\i\)}\)\),(?=\i)/g,
|
||||||
|
replace: "$&$self.CompactPronounsChatComponentWrapper(arguments[0]),",
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ function VoiceChannelTooltip({ channel, isLocked }: VoiceChannelTooltipProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface VoiceChannelIndicatorProps {
|
export interface VoiceChannelIndicatorProps {
|
||||||
userId: string;
|
userId: string;
|
||||||
isActionButton?: boolean;
|
isActionButton?: boolean;
|
||||||
shouldHighlight?: boolean;
|
shouldHighlight?: boolean;
|
||||||
|
|
|
@ -193,10 +193,18 @@ export default definePlugin({
|
||||||
// Avatar component used in User DMs "User Profile" popup in the right and Profiles Modal pfp
|
// Avatar component used in User DMs "User Profile" popup in the right and Profiles Modal pfp
|
||||||
{
|
{
|
||||||
find: ".overlay:void 0,status:",
|
find: ".overlay:void 0,status:",
|
||||||
replacement: {
|
replacement: [
|
||||||
match: /avatarSrc:(\i),eventHandlers:(\i).+?"div",{...\2,/,
|
{
|
||||||
replace: "$&style:{cursor:\"pointer\"},onClick:()=>{$self.openAvatar($1)},"
|
// FIXME(Bundler spread transform related): Remove old compatiblity once enough time has passed, if they don't revert
|
||||||
},
|
match: /avatarSrc:(\i),eventHandlers:(\i).+?"div",{...\2,/,
|
||||||
|
replace: "$&style:{cursor:\"pointer\"},onClick:()=>{$self.openAvatar($1)},",
|
||||||
|
noWarn: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /avatarSrc:(\i),eventHandlers:(\i).+?"div",.{0,100}className:\i,/,
|
||||||
|
replace: "$&style:{cursor:\"pointer\"},onClick:()=>{$self.openAvatar($1)},",
|
||||||
|
}
|
||||||
|
],
|
||||||
all: true
|
all: true
|
||||||
},
|
},
|
||||||
// Banners
|
// Banners
|
||||||
|
|
|
@ -10,7 +10,7 @@ import adguard from "file://adguard.js?minify";
|
||||||
|
|
||||||
app.on("browser-window-created", (_, win) => {
|
app.on("browser-window-created", (_, win) => {
|
||||||
win.webContents.on("frame-created", (_, { frame }) => {
|
win.webContents.on("frame-created", (_, { frame }) => {
|
||||||
frame.once("dom-ready", () => {
|
frame?.once("dom-ready", () => {
|
||||||
if (!RendererSettings.store.plugins?.YoutubeAdblock?.enabled) return;
|
if (!RendererSettings.store.plugins?.YoutubeAdblock?.enabled) return;
|
||||||
|
|
||||||
if (frame.url.includes("youtube.com/embed/") || (frame.url.includes("discordsays") && frame.url.includes("youtube.com"))) {
|
if (frame.url.includes("youtube.com/embed/") || (frame.url.includes("discordsays") && frame.url.includes("youtube.com"))) {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
@media(width <= 485px) {
|
@media(width <= 485px) {
|
||||||
.vc-image-modal {
|
.vc-image-modal {
|
||||||
display: relative;
|
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
overflow: initial;
|
overflow: initial;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,9 @@ export function makeLazy<T>(factory: () => T, attempts = 5): () => T {
|
||||||
let tries = 0;
|
let tries = 0;
|
||||||
let cache: T;
|
let cache: T;
|
||||||
return () => {
|
return () => {
|
||||||
if (!cache && attempts > tries++) {
|
if (cache === undefined && attempts > tries++) {
|
||||||
cache = factory();
|
cache = factory();
|
||||||
if (!cache && attempts === tries)
|
if (cache === undefined && attempts === tries)
|
||||||
console.error("Lazy factory failed:", factory);
|
console.error("Lazy factory failed:", factory);
|
||||||
}
|
}
|
||||||
return cache;
|
return cache;
|
||||||
|
|
|
@ -42,7 +42,12 @@ export interface PatchReplacement {
|
||||||
match: string | RegExp;
|
match: string | RegExp;
|
||||||
/** The replacement string or function which returns the string for the patch replacement */
|
/** The replacement string or function which returns the string for the patch replacement */
|
||||||
replace: string | ReplaceFn;
|
replace: string | ReplaceFn;
|
||||||
/** A function which returns whether this patch replacement should be applied */
|
/** Do not warn if this replacement did no changes */
|
||||||
|
noWarn?: boolean;
|
||||||
|
/**
|
||||||
|
* A function which returns whether this patch replacement should be applied.
|
||||||
|
* This is ran before patches are registered, so if this returns false, the patch will never be registered.
|
||||||
|
*/
|
||||||
predicate?(): boolean;
|
predicate?(): boolean;
|
||||||
/** The minimum build number for this patch to be applied */
|
/** The minimum build number for this patch to be applied */
|
||||||
fromBuild?: number;
|
fromBuild?: number;
|
||||||
|
@ -62,7 +67,10 @@ export interface Patch {
|
||||||
noWarn?: boolean;
|
noWarn?: boolean;
|
||||||
/** Only apply this set of replacements if all of them succeed. Use this if your replacements depend on each other */
|
/** Only apply this set of replacements if all of them succeed. Use this if your replacements depend on each other */
|
||||||
group?: boolean;
|
group?: boolean;
|
||||||
/** A function which returns whether this patch should be applied */
|
/**
|
||||||
|
* A function which returns whether this patch replacement should be applied.
|
||||||
|
* This is ran before patches are registered, so if this returns false, the patch will never be registered.
|
||||||
|
*/
|
||||||
predicate?(): boolean;
|
predicate?(): boolean;
|
||||||
/** The minimum build number for this patch to be applied */
|
/** The minimum build number for this patch to be applied */
|
||||||
fromBuild?: number;
|
fromBuild?: number;
|
||||||
|
|
|
@ -41,7 +41,7 @@ export const Switch = waitForComponent<t.Switch>("Switch", filters.componentByCo
|
||||||
|
|
||||||
const Tooltips = mapMangledModuleLazy(".tooltipTop,bottom:", {
|
const Tooltips = mapMangledModuleLazy(".tooltipTop,bottom:", {
|
||||||
Tooltip: filters.componentByCode("this.renderTooltip()]"),
|
Tooltip: filters.componentByCode("this.renderTooltip()]"),
|
||||||
TooltipContainer: filters.componentByCode('="div",')
|
TooltipContainer: filters.componentByCode('="div"')
|
||||||
}) as {
|
}) as {
|
||||||
Tooltip: t.Tooltip,
|
Tooltip: t.Tooltip,
|
||||||
TooltipContainer: t.TooltipContainer;
|
TooltipContainer: t.TooltipContainer;
|
||||||
|
|
|
@ -29,7 +29,7 @@ export type GenericStore = t.FluxStore & Record<string, any>;
|
||||||
|
|
||||||
export const DraftType = findByPropsLazy("ChannelMessage", "SlashCommand");
|
export const DraftType = findByPropsLazy("ChannelMessage", "SlashCommand");
|
||||||
|
|
||||||
export let MessageStore: Omit<Stores.MessageStore, "getMessages"> & {
|
export let MessageStore: Omit<Stores.MessageStore, "getMessages"> & GenericStore & {
|
||||||
getMessages(chanId: string): any;
|
getMessages(chanId: string): any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
2
src/webpack/common/types/components.d.ts
vendored
2
src/webpack/common/types/components.d.ts
vendored
|
@ -496,7 +496,7 @@ export type Avatar = ComponentType<PropsWithChildren<{
|
||||||
}>>;
|
}>>;
|
||||||
|
|
||||||
type FocusLock = ComponentType<PropsWithChildren<{
|
type FocusLock = ComponentType<PropsWithChildren<{
|
||||||
containerRef: RefObject<HTMLElement>;
|
containerRef: Ref<HTMLElement>;
|
||||||
}>>;
|
}>>;
|
||||||
|
|
||||||
export type Icon = ComponentType<JSX.IntrinsicElements["svg"] & {
|
export type Icon = ComponentType<JSX.IntrinsicElements["svg"] & {
|
||||||
|
|
3
src/webpack/common/types/stores.d.ts
vendored
3
src/webpack/common/types/stores.d.ts
vendored
|
@ -16,7 +16,6 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { DraftType } from "@webpack/common";
|
|
||||||
import { Channel, Guild, Role } from "discord-types/general";
|
import { Channel, Guild, Role } from "discord-types/general";
|
||||||
|
|
||||||
import { FluxDispatcher, FluxEvents } from "./utils";
|
import { FluxDispatcher, FluxEvents } from "./utils";
|
||||||
|
@ -234,7 +233,7 @@ export class PopoutWindowStore extends FluxStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type useStateFromStores = <T>(
|
export type useStateFromStores = <T>(
|
||||||
stores: t.FluxStore[],
|
stores: any[],
|
||||||
mapper: () => T,
|
mapper: () => T,
|
||||||
dependencies?: any,
|
dependencies?: any,
|
||||||
isEqual?: (old: T, newer: T) => boolean
|
isEqual?: (old: T, newer: T) => boolean
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { runtimeHashMessageKey } from "@utils/intlHash";
|
|
||||||
import type { Channel } from "discord-types/general";
|
import type { Channel } from "discord-types/general";
|
||||||
|
|
||||||
// eslint-disable-next-line path-alias/no-relative
|
// eslint-disable-next-line path-alias/no-relative
|
||||||
|
@ -58,9 +57,9 @@ export const { match, P }: Pick<typeof import("ts-pattern"), "match" | "P"> = ma
|
||||||
export const lodash: typeof import("lodash") = findByPropsLazy("debounce", "cloneDeep");
|
export const lodash: typeof import("lodash") = findByPropsLazy("debounce", "cloneDeep");
|
||||||
|
|
||||||
export const i18n = mapMangledModuleLazy('defaultLocale:"en-US"', {
|
export const i18n = mapMangledModuleLazy('defaultLocale:"en-US"', {
|
||||||
intl: filters.byProps("string", "format"),
|
t: m => m?.[Symbol.toStringTag] === "IntlMessagesProxy",
|
||||||
t: filters.byProps(runtimeHashMessageKey("DISCORD"))
|
intl: m => m != null && Object.getPrototypeOf(m)?.withFormatters != null
|
||||||
});
|
}, true);
|
||||||
|
|
||||||
export let SnowflakeUtils: t.SnowflakeUtils;
|
export let SnowflakeUtils: t.SnowflakeUtils;
|
||||||
waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m);
|
waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m);
|
||||||
|
|
|
@ -9,63 +9,63 @@ import { makeLazy } from "@utils/lazy";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { interpolateIfDefined } from "@utils/misc";
|
import { interpolateIfDefined } from "@utils/misc";
|
||||||
import { canonicalizeReplacement } from "@utils/patches";
|
import { canonicalizeReplacement } from "@utils/patches";
|
||||||
import { PatchReplacement } from "@utils/types";
|
import { Patch, PatchReplacement } from "@utils/types";
|
||||||
|
|
||||||
import { traceFunctionWithResults } from "../debug/Tracer";
|
import { traceFunctionWithResults } from "../debug/Tracer";
|
||||||
import { patches } from "../plugins";
|
import { _blacklistBadModules, _initWebpack, factoryListeners, findModuleFactory, moduleListeners, waitForSubscriptions, wreq } from "./webpack";
|
||||||
import { _initWebpack, _shouldIgnoreModule, AnyModuleFactory, AnyWebpackRequire, factoryListeners, findModuleId, MaybeWrappedModuleFactory, ModuleExports, moduleListeners, waitForSubscriptions, WebpackRequire, WrappedModuleFactory, wreq } from ".";
|
import { AnyModuleFactory, AnyWebpackRequire, MaybePatchedModuleFactory, PatchedModuleFactory, WebpackRequire } from "./wreq.d";
|
||||||
|
|
||||||
|
export const patches = [] as Patch[];
|
||||||
|
|
||||||
|
export const SYM_IS_PROXIED_FACTORY = Symbol("WebpackPatcher.isProxiedFactory");
|
||||||
export const SYM_ORIGINAL_FACTORY = Symbol("WebpackPatcher.originalFactory");
|
export const SYM_ORIGINAL_FACTORY = Symbol("WebpackPatcher.originalFactory");
|
||||||
export const SYM_PATCHED_SOURCE = Symbol("WebpackPatcher.patchedSource");
|
export const SYM_PATCHED_SOURCE = Symbol("WebpackPatcher.patchedSource");
|
||||||
export const SYM_PATCHED_BY = Symbol("WebpackPatcher.patchedBy");
|
export const SYM_PATCHED_BY = Symbol("WebpackPatcher.patchedBy");
|
||||||
/** A set with all the Webpack instances */
|
|
||||||
export const allWebpackInstances = new Set<AnyWebpackRequire>();
|
export const allWebpackInstances = new Set<AnyWebpackRequire>();
|
||||||
export const patchTimings = [] as Array<[plugin: string, moduleId: PropertyKey, match: string | RegExp, totalTime: number]>;
|
|
||||||
|
|
||||||
const logger = new Logger("WebpackInterceptor", "#8caaee");
|
export const patchTimings = [] as Array<[plugin: string, moduleId: PropertyKey, match: PatchReplacement["match"], totalTime: number]>;
|
||||||
/** Whether we tried to fallback to factory WebpackRequire, or disabled patches */
|
|
||||||
let wreqFallbackApplied = false;
|
|
||||||
/** Whether we should be patching factories.
|
|
||||||
*
|
|
||||||
* This should be disabled if we start searching for the module to get the build number, and then resumed once it's done.
|
|
||||||
* */
|
|
||||||
let shouldPatchFactories = true;
|
|
||||||
|
|
||||||
export const getBuildNumber = makeLazy(() => {
|
export const getBuildNumber = makeLazy(() => {
|
||||||
try {
|
try {
|
||||||
shouldPatchFactories = false;
|
function matchBuildNumber(factoryStr: string) {
|
||||||
|
const buildNumberMatch = factoryStr.match(/.concat\("(\d+?)"\)/);
|
||||||
try {
|
if (buildNumberMatch == null) {
|
||||||
if (wreq.m[128014]?.toString().includes("Trying to open a changelog for an invalid build number")) {
|
return -1;
|
||||||
const hardcodedGetBuildNumber = wreq(128014).b as () => number;
|
|
||||||
|
|
||||||
if (typeof hardcodedGetBuildNumber === "function" && typeof hardcodedGetBuildNumber() === "number") {
|
|
||||||
return hardcodedGetBuildNumber();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch { }
|
|
||||||
|
|
||||||
const moduleId = findModuleId("Trying to open a changelog for an invalid build number");
|
return Number(buildNumberMatch[1]);
|
||||||
if (moduleId == null) {
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const exports = Object.values<ModuleExports>(wreq(moduleId));
|
const hardcodedFactoryStr = String(wreq.m[128014]);
|
||||||
if (exports.length !== 1 || typeof exports[0] !== "function") {
|
if (hardcodedFactoryStr.includes("Trying to open a changelog for an invalid build number")) {
|
||||||
return -1;
|
const hardcodedBuildNumber = matchBuildNumber(hardcodedFactoryStr);
|
||||||
|
|
||||||
|
if (hardcodedBuildNumber !== -1) {
|
||||||
|
return hardcodedBuildNumber;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildNumber = exports[0]();
|
const moduleFactory = findModuleFactory("Trying to open a changelog for an invalid build number");
|
||||||
return typeof buildNumber === "number" ? buildNumber : -1;
|
return matchBuildNumber(String(moduleFactory));
|
||||||
} catch {
|
} catch {
|
||||||
return -1;
|
return -1;
|
||||||
} finally {
|
|
||||||
shouldPatchFactories = true;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
type Define = typeof Reflect.defineProperty;
|
export function getFactoryPatchedSource(moduleId: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) {
|
||||||
const define: Define = (target, p, attributes) => {
|
return webpackRequire.m[moduleId]?.[SYM_PATCHED_SOURCE];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFactoryPatchedBy(moduleId: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) {
|
||||||
|
return webpackRequire.m[moduleId]?.[SYM_PATCHED_BY];
|
||||||
|
}
|
||||||
|
|
||||||
|
const logger = new Logger("WebpackInterceptor", "#8caaee");
|
||||||
|
|
||||||
|
/** Whether we tried to fallback to the WebpackRequire of the factory, or disabled patches */
|
||||||
|
let wreqFallbackApplied = false;
|
||||||
|
|
||||||
|
const define: typeof Reflect.defineProperty = (target, p, attributes) => {
|
||||||
if (Object.hasOwn(attributes, "value")) {
|
if (Object.hasOwn(attributes, "value")) {
|
||||||
attributes.writable = true;
|
attributes.writable = true;
|
||||||
}
|
}
|
||||||
|
@ -77,22 +77,18 @@ const define: Define = (target, p, attributes) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getOriginalFactory(id: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) {
|
// wreq.m is the Webpack object containing module factories. It is pre-populated with factories, and is also populated via webpackGlobal.push
|
||||||
const moduleFactory = webpackRequire.m[id];
|
// We use this setter to intercept when wreq.m is defined and setup our setters which decide whether we should patch these module factories
|
||||||
return (moduleFactory?.[SYM_ORIGINAL_FACTORY] ?? moduleFactory) as AnyModuleFactory | undefined;
|
// and the Webpack instance where they are being defined.
|
||||||
}
|
|
||||||
|
|
||||||
export function getFactoryPatchedSource(id: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) {
|
// Factories can be patched in two ways. Eagerly or lazily.
|
||||||
return webpackRequire.m[id]?.[SYM_PATCHED_SOURCE];
|
// If we are patching eagerly, pre-populated factories are patched immediately and new factories are patched when set.
|
||||||
}
|
// Else, we only patch them when called.
|
||||||
|
|
||||||
export function getFactoryPatchedBy(id: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) {
|
// Factories are always wrapped in a proxy, which allows us to intercept the call to them, patch if they werent eagerly patched,
|
||||||
return webpackRequire.m[id]?.[SYM_PATCHED_BY];
|
// and call them with our wrapper which notifies our listeners.
|
||||||
}
|
|
||||||
|
|
||||||
// wreq.m is the Webpack object containing module factories. It is pre-populated with module factories, and is also populated via webpackGlobal.push
|
// wreq.m is also wrapped in a proxy to intercept when new factories are set, patch them eargely, if enabled, and wrap them in the factory proxy.
|
||||||
// We use this setter to intercept when wreq.m is defined and apply the patching in its module factories.
|
|
||||||
// We wrap wreq.m with our proxy, which is responsible for patching the module factories when they are set, or defining getters for the patched versions.
|
|
||||||
|
|
||||||
// If this is the main Webpack, we also set up the internal references to WebpackRequire.
|
// If this is the main Webpack, we also set up the internal references to WebpackRequire.
|
||||||
define(Function.prototype, "m", {
|
define(Function.prototype, "m", {
|
||||||
|
@ -101,7 +97,7 @@ define(Function.prototype, "m", {
|
||||||
set(this: AnyWebpackRequire, originalModules: AnyWebpackRequire["m"]) {
|
set(this: AnyWebpackRequire, originalModules: AnyWebpackRequire["m"]) {
|
||||||
define(this, "m", { value: originalModules });
|
define(this, "m", { value: originalModules });
|
||||||
|
|
||||||
// Ensure this is one of Discord main Webpack instances.
|
// Ensure this is likely one of Discord main Webpack instances.
|
||||||
// We may catch Discord bundled libs, React Devtools or other extensions Webpack instances here.
|
// We may catch Discord bundled libs, React Devtools or other extensions Webpack instances here.
|
||||||
const { stack } = new Error();
|
const { stack } = new Error();
|
||||||
if (!stack?.includes("http") || stack.match(/at \d+? \(/) || !String(this).includes("exports:{}")) {
|
if (!stack?.includes("http") || stack.match(/at \d+? \(/) || !String(this).includes("exports:{}")) {
|
||||||
|
@ -109,53 +105,108 @@ define(Function.prototype, "m", {
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1];
|
const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1];
|
||||||
logger.info("Found Webpack module factories" + interpolateIfDefined` in ${fileName}`);
|
|
||||||
|
|
||||||
allWebpackInstances.add(this);
|
// Define a setter for the bundlePath property of WebpackRequire. Only Webpack instances which include chunk loading functionality,
|
||||||
|
// like the main Discord Webpack, have this property.
|
||||||
// Define a setter for the ensureChunk property of WebpackRequire. Only the main Webpack (which is the only that includes chunk loading) has this property.
|
// So if the setter is called with the Discord bundlePath, this means we should patch this instance and initialize the internal references to WebpackRequire.
|
||||||
// So if the setter is called, this means we can initialize the internal references to WebpackRequire.
|
define(this, "p", {
|
||||||
define(this, "e", {
|
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
|
|
||||||
set(this: WebpackRequire, ensureChunk: WebpackRequire["e"]) {
|
set(this: AnyWebpackRequire, bundlePath: NonNullable<AnyWebpackRequire["p"]>) {
|
||||||
define(this, "e", { value: ensureChunk });
|
define(this, "p", { value: bundlePath });
|
||||||
clearTimeout(setterTimeout);
|
clearTimeout(bundlePathTimeout);
|
||||||
|
|
||||||
logger.info("Main WebpackInstance found" + interpolateIfDefined` in ${fileName}` + ", initializing internal references to WebpackRequire");
|
if (bundlePath !== "/assets/") {
|
||||||
_initWebpack(this);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wreq == null && this.c != null) {
|
||||||
|
logger.info("Main WebpackInstance found" + interpolateIfDefined` in ${fileName}` + ", initializing internal references to WebpackRequire");
|
||||||
|
_initWebpack(this as WebpackRequire);
|
||||||
|
}
|
||||||
|
|
||||||
|
patchThisInstance();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// setImmediate to clear this property setter if this is not the main Webpack.
|
|
||||||
// If this is the main Webpack, wreq.e will always be set before the timeout runs.
|
|
||||||
const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "e"), 0);
|
|
||||||
|
|
||||||
// Patch the pre-populated factories
|
// In the past, the sentry Webpack instance which we also wanted to patch used to rely on chunks being loaded before initting sentry.
|
||||||
for (const id in originalModules) {
|
// This Webpack instance did not include actual chunk loading, and only awaited for them to be loaded, which means it did not include the bundlePath property.
|
||||||
if (updateExistingFactory(originalModules, id, originalModules[id], true)) {
|
// To keep backwards compability, in case this is ever the case again, and keep patching this type of instance, we explicity patch instances which include wreq.O and not wreq.p.
|
||||||
continue;
|
// Since we cannot check what is the bundlePath of the instance to filter for the Discord bundlePath, we only patch it if wreq.p is not included,
|
||||||
|
// which means the instance relies on another instance which does chunk loading, and that makes it very likely to only target Discord Webpack instances like the old sentry.
|
||||||
|
|
||||||
|
// Instead of patching when wreq.O is defined, wait for when wreq.O.j is defined, since that will be one of the last things to happen,
|
||||||
|
// which can assure wreq.p could have already been defined before.
|
||||||
|
define(this, "O", {
|
||||||
|
enumerable: false,
|
||||||
|
|
||||||
|
set(this: AnyWebpackRequire, onChunksLoaded: NonNullable<AnyWebpackRequire["O"]>) {
|
||||||
|
define(this, "O", { value: onChunksLoaded });
|
||||||
|
clearTimeout(onChunksLoadedTimeout);
|
||||||
|
|
||||||
|
const wreq = this;
|
||||||
|
define(onChunksLoaded, "j", {
|
||||||
|
enumerable: false,
|
||||||
|
|
||||||
|
set(this: NonNullable<AnyWebpackRequire["O"]>, j: NonNullable<AnyWebpackRequire["O"]>["j"]) {
|
||||||
|
define(this, "j", { value: j });
|
||||||
|
|
||||||
|
if (wreq.p == null) {
|
||||||
|
patchThisInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyFactoryListeners(originalModules[id]);
|
|
||||||
defineModulesFactoryGetter(id, Settings.eagerPatches && shouldPatchFactories ? wrapAndPatchFactory(id, originalModules[id]) : originalModules[id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
define(originalModules, Symbol.toStringTag, {
|
|
||||||
value: "ModuleFactories",
|
|
||||||
enumerable: false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// The proxy responsible for patching the module factories when they are set, or defining getters for the patched versions
|
// If neither of these properties setters were triggered, delete them as they are not needed anymore.
|
||||||
const proxiedModuleFactories = new Proxy(originalModules, moduleFactoriesHandler);
|
const bundlePathTimeout = setTimeout(() => Reflect.deleteProperty(this, "p"), 0);
|
||||||
/*
|
const onChunksLoadedTimeout = setTimeout(() => Reflect.deleteProperty(this, "O"), 0);
|
||||||
If Webpack ever decides to set module factories using the variable of the modules object directly, instead of wreq.m, switch the proxy to the prototype
|
|
||||||
Reflect.setPrototypeOf(originalModules, new Proxy(originalModules, moduleFactoriesHandler));
|
|
||||||
*/
|
|
||||||
|
|
||||||
define(this, "m", { value: proxiedModuleFactories });
|
/**
|
||||||
|
* Patch the current Webpack instance assigned to `this` context.
|
||||||
|
* This should only be called if this instance was later found to be one we need to patch.
|
||||||
|
*/
|
||||||
|
const patchThisInstance = () => {
|
||||||
|
logger.info("Found Webpack module factories" + interpolateIfDefined` in ${fileName}`);
|
||||||
|
allWebpackInstances.add(this);
|
||||||
|
|
||||||
|
// Proxy (and maybe patch) pre-populated factories
|
||||||
|
for (const moduleId in originalModules) {
|
||||||
|
updateExistingOrProxyFactory(originalModules, moduleId, originalModules[moduleId], originalModules, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
define(originalModules, Symbol.toStringTag, {
|
||||||
|
value: "ModuleFactories",
|
||||||
|
enumerable: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const proxiedModuleFactories = new Proxy(originalModules, moduleFactoriesHandler);
|
||||||
|
/*
|
||||||
|
If Webpack ever decides to set module factories using the variable of the modules object directly, instead of wreq.m, switch the proxy to the prototype
|
||||||
|
Reflect.setPrototypeOf(originalModules, new Proxy(originalModules, moduleFactoriesHandler));
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(this, "m", { value: proxiedModuleFactories });
|
||||||
|
|
||||||
|
// Overwrite Webpack's defineExports function to define the export descriptors configurable.
|
||||||
|
// This is needed so we can later blacklist specific exports from Webpack search by making them non-enumerable
|
||||||
|
this.d = function (exports, definition) {
|
||||||
|
for (const key in definition) {
|
||||||
|
if (Object.hasOwn(definition, key) && !Object.hasOwn(exports, key)) {
|
||||||
|
Object.defineProperty(exports, key, {
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
|
get: definition[key],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The proxy for patching eagerly and/or wrapping factories in their proxy.
|
||||||
const moduleFactoriesHandler: ProxyHandler<AnyWebpackRequire["m"]> = {
|
const moduleFactoriesHandler: ProxyHandler<AnyWebpackRequire["m"]> = {
|
||||||
/*
|
/*
|
||||||
If Webpack ever decides to set module factories using the variable of the modules object directly instead of wreq.m, we need to switch the proxy to the prototype
|
If Webpack ever decides to set module factories using the variable of the modules object directly instead of wreq.m, we need to switch the proxy to the prototype
|
||||||
|
@ -172,71 +223,127 @@ const moduleFactoriesHandler: ProxyHandler<AnyWebpackRequire["m"]> = {
|
||||||
},
|
},
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// The set trap for patching or defining getters for the module factories when new module factories are loaded
|
set: updateExistingOrProxyFactory
|
||||||
set(target, p, newValue, receiver) {
|
};
|
||||||
if (updateExistingFactory(target, p, newValue)) {
|
|
||||||
|
// The proxy for patching lazily and/or running factories with our wrapper.
|
||||||
|
const moduleFactoryHandler: ProxyHandler<MaybePatchedModuleFactory> = {
|
||||||
|
apply(target, thisArg: unknown, argArray: Parameters<AnyModuleFactory>) {
|
||||||
|
// SYM_ORIGINAL_FACTORY means the factory has already been patched
|
||||||
|
if (target[SYM_ORIGINAL_FACTORY] != null) {
|
||||||
|
return runFactoryWithWrap(target as PatchedModuleFactory, thisArg, argArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: Factories have `name` as their key in the module factories object, and that is always their module id
|
||||||
|
const moduleId: string = target.name;
|
||||||
|
|
||||||
|
const patchedFactory = patchFactory(moduleId, target);
|
||||||
|
return runFactoryWithWrap(patchedFactory, thisArg, argArray);
|
||||||
|
},
|
||||||
|
|
||||||
|
get(target, p, receiver) {
|
||||||
|
if (p === SYM_IS_PROXIED_FACTORY) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyFactoryListeners(newValue);
|
const originalFactory: AnyModuleFactory = target[SYM_ORIGINAL_FACTORY] ?? target;
|
||||||
defineModulesFactoryGetter(p, Settings.eagerPatches && shouldPatchFactories ? wrapAndPatchFactory(p, newValue) : newValue);
|
|
||||||
|
|
||||||
return true;
|
// Redirect these properties to the original factory, including making `toString` return the original factory `toString`
|
||||||
|
if (p === "toString" || p === SYM_PATCHED_SOURCE || p === SYM_PATCHED_BY) {
|
||||||
|
const v = Reflect.get(originalFactory, p, originalFactory);
|
||||||
|
return p === "toString" ? v.bind(originalFactory) : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Reflect.get(target, p, receiver);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function updateExistingOrProxyFactory(moduleFactories: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, receiver: any, ignoreExistingInTarget = false) {
|
||||||
|
if (updateExistingFactory(moduleFactories, moduleId, newFactory, receiver, ignoreExistingInTarget)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyFactoryListeners(moduleId, newFactory);
|
||||||
|
|
||||||
|
const proxiedFactory = new Proxy(Settings.eagerPatches ? patchFactory(moduleId, newFactory) : newFactory, moduleFactoryHandler);
|
||||||
|
return Reflect.set(moduleFactories, moduleId, proxiedFactory, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a factory that exists in any Webpack instance with a new original factory.
|
* Update a duplicated factory that exists in any of the Webpack instances we track with a new original factory.
|
||||||
*
|
*
|
||||||
* @target The module factories where this new original factory is being set
|
* @param moduleFactories The module factories where this new original factory is being set
|
||||||
* @param id The id of the module
|
* @param moduleId The id of the module
|
||||||
* @param newFactory The new original factory
|
* @param newFactory The new original factory
|
||||||
* @param ignoreExistingInTarget Whether to ignore checking if the factory already exists in the moduleFactoriesTarget
|
* @param receiver The receiver of the factory
|
||||||
* @returns Whether the original factory was updated, or false if it doesn't exist in any Webpack instance
|
* @param ignoreExistingInTarget Whether to ignore checking if the factory already exists in the moduleFactories where it is being set
|
||||||
|
* @returns Whether the original factory was updated, or false if it doesn't exist in any of the tracked Webpack instances
|
||||||
*/
|
*/
|
||||||
function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], id: PropertyKey, newFactory: AnyModuleFactory, ignoreExistingInTarget: boolean = false) {
|
function updateExistingFactory(moduleFactories: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, receiver: any, ignoreExistingInTarget) {
|
||||||
let existingFactory: TypedPropertyDescriptor<AnyModuleFactory> | undefined;
|
let existingFactory: AnyModuleFactory | undefined;
|
||||||
let moduleFactoriesWithFactory: AnyWebpackRequire["m"] | undefined;
|
let moduleFactoriesWithFactory: AnyWebpackRequire["m"] | undefined;
|
||||||
for (const wreq of allWebpackInstances) {
|
for (const wreq of allWebpackInstances) {
|
||||||
if (ignoreExistingInTarget && wreq.m === moduleFactoriesTarget) continue;
|
if (ignoreExistingInTarget && wreq.m === moduleFactories) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (Object.hasOwn(wreq.m, id)) {
|
if (Object.hasOwn(wreq.m, moduleId)) {
|
||||||
existingFactory = Reflect.getOwnPropertyDescriptor(wreq.m, id);
|
existingFactory = wreq.m[moduleId];
|
||||||
moduleFactoriesWithFactory = wreq.m;
|
moduleFactoriesWithFactory = wreq.m;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingFactory != null) {
|
if (existingFactory != null) {
|
||||||
// If existingFactory exists in any Webpack instance, it's either wrapped in defineModuleFactoryGetter, or it has already been required.
|
// If existingFactory exists in any of the Webpack instances we track, it's either wrapped in our proxy, or it has already been required.
|
||||||
// So define the descriptor of it on this current Webpack instance (if it doesn't exist already), call Reflect.set with the new original,
|
// In the case it is wrapped in our proxy, and the instance we are setting does not already have it, we need to make sure the instance contains our proxy too.
|
||||||
// and let the correct logic apply (normal set, or defineModuleFactoryGetter setter)
|
if (moduleFactoriesWithFactory !== moduleFactories && existingFactory[SYM_IS_PROXIED_FACTORY]) {
|
||||||
|
Reflect.set(moduleFactories, moduleId, existingFactory, receiver);
|
||||||
if (moduleFactoriesWithFactory !== moduleFactoriesTarget) {
|
}
|
||||||
Reflect.defineProperty(moduleFactoriesTarget, id, existingFactory);
|
// Else, if it is not wrapped in our proxy, set this new original factory in all the instances
|
||||||
|
else {
|
||||||
|
defineInWebpackInstances(moduleId, newFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Persist patched source and patched by in the new original factory, if the patched one has already been required
|
// Update existingFactory with the new original, if it does have a current original factory
|
||||||
if (IS_DEV && existingFactory.value != null) {
|
if (existingFactory[SYM_ORIGINAL_FACTORY] != null) {
|
||||||
newFactory[SYM_PATCHED_SOURCE] = existingFactory.value[SYM_PATCHED_SOURCE];
|
existingFactory[SYM_ORIGINAL_FACTORY] = newFactory;
|
||||||
newFactory[SYM_PATCHED_BY] = existingFactory.value[SYM_PATCHED_BY];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Reflect.set(moduleFactoriesTarget, id, newFactory, moduleFactoriesTarget);
|
// Persist patched source and patched by in the new original factory
|
||||||
|
if (IS_DEV) {
|
||||||
|
newFactory[SYM_PATCHED_SOURCE] = existingFactory[SYM_PATCHED_SOURCE];
|
||||||
|
newFactory[SYM_PATCHED_BY] = existingFactory[SYM_PATCHED_BY];
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a module factory in all the Webpack instances we track.
|
||||||
|
*
|
||||||
|
* @param moduleId The id of the module
|
||||||
|
* @param factory The factory
|
||||||
|
*/
|
||||||
|
function defineInWebpackInstances(moduleId: PropertyKey, factory: AnyModuleFactory) {
|
||||||
|
for (const wreq of allWebpackInstances) {
|
||||||
|
define(wreq.m, moduleId, { value: factory });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify all factory listeners.
|
* Notify all factory listeners.
|
||||||
*
|
*
|
||||||
|
* @param moduleId The id of the module
|
||||||
* @param factory The original factory to notify for
|
* @param factory The original factory to notify for
|
||||||
*/
|
*/
|
||||||
function notifyFactoryListeners(factory: AnyModuleFactory) {
|
function notifyFactoryListeners(moduleId: PropertyKey, factory: AnyModuleFactory) {
|
||||||
for (const factoryListener of factoryListeners) {
|
for (const factoryListener of factoryListeners) {
|
||||||
try {
|
try {
|
||||||
factoryListener(factory);
|
factoryListener(factory, moduleId);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error("Error in Webpack factory listener:\n", err, factoryListener);
|
logger.error("Error in Webpack factory listener:\n", err, factoryListener);
|
||||||
}
|
}
|
||||||
|
@ -244,213 +351,148 @@ function notifyFactoryListeners(factory: AnyModuleFactory) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the getter for returning the patched version of the module factory.
|
* Run a (possibly) patched module factory with a wrapper which notifies our listeners.
|
||||||
*
|
*
|
||||||
* If eagerPatches is enabled, the factory argument should already be the patched version, else it will be the original
|
* @param patchedFactory The (possibly) patched module factory
|
||||||
* and only be patched when accessed for the first time.
|
* @param thisArg The `value` of the call to the factory
|
||||||
*
|
* @param argArray The arguments of the call to the factory
|
||||||
* @param id The id of the module
|
|
||||||
* @param factory The original or patched module factory
|
|
||||||
*/
|
*/
|
||||||
function defineModulesFactoryGetter(id: PropertyKey, factory: MaybeWrappedModuleFactory) {
|
function runFactoryWithWrap(patchedFactory: PatchedModuleFactory, thisArg: unknown, argArray: Parameters<MaybePatchedModuleFactory>) {
|
||||||
const descriptor: PropertyDescriptor = {
|
const originalFactory = patchedFactory[SYM_ORIGINAL_FACTORY];
|
||||||
get() {
|
|
||||||
// SYM_ORIGINAL_FACTORY means the factory is already patched
|
|
||||||
if (!shouldPatchFactories || factory[SYM_ORIGINAL_FACTORY] != null) {
|
|
||||||
return factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (factory = wrapAndPatchFactory(id, factory));
|
if (patchedFactory === originalFactory) {
|
||||||
},
|
// @ts-expect-error Clear up ORIGINAL_FACTORY if the factory did not have any patch applied
|
||||||
set(newFactory: MaybeWrappedModuleFactory) {
|
delete patchedFactory[SYM_ORIGINAL_FACTORY];
|
||||||
if (IS_DEV) {
|
|
||||||
newFactory[SYM_PATCHED_SOURCE] = factory[SYM_PATCHED_SOURCE];
|
|
||||||
newFactory[SYM_PATCHED_BY] = factory[SYM_PATCHED_BY];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (factory[SYM_ORIGINAL_FACTORY] != null) {
|
|
||||||
factory.toString = newFactory.toString.bind(newFactory);
|
|
||||||
factory[SYM_ORIGINAL_FACTORY] = newFactory;
|
|
||||||
} else {
|
|
||||||
factory = newFactory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Define the getter in all the module factories objects. Patches are only executed once, so make sure all module factories object
|
|
||||||
// have the patched version
|
|
||||||
for (const wreq of allWebpackInstances) {
|
|
||||||
define(wreq.m, id, descriptor);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
let [module, exports, require] = argArray;
|
||||||
* Wraps and patches a module factory.
|
|
||||||
*
|
|
||||||
* @param id The id of the module
|
|
||||||
* @param factory The original or patched module factory
|
|
||||||
* @returns The wrapper for the patched module factory
|
|
||||||
*/
|
|
||||||
function wrapAndPatchFactory(id: PropertyKey, originalFactory: AnyModuleFactory) {
|
|
||||||
const [patchedFactory, patchedSource, patchedBy] = patchFactory(id, originalFactory);
|
|
||||||
|
|
||||||
const wrappedFactory: WrappedModuleFactory = function (...args) {
|
// Restore the original factory in all the module factories objects, discarding our proxy and allowing it to be garbage collected
|
||||||
// Restore the original factory in all the module factories objects. We want to make sure the original factory is restored properly, no matter what is the Webpack instance
|
defineInWebpackInstances(module.id, originalFactory);
|
||||||
for (const wreq of allWebpackInstances) {
|
|
||||||
define(wreq.m, id, { value: wrappedFactory[SYM_ORIGINAL_FACTORY] });
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line prefer-const
|
if (wreq == null) {
|
||||||
let [module, exports, require] = args;
|
if (!wreqFallbackApplied) {
|
||||||
|
wreqFallbackApplied = true;
|
||||||
|
|
||||||
if (wreq == null) {
|
// Make sure the require argument is actually the WebpackRequire function
|
||||||
if (!wreqFallbackApplied) {
|
if (typeof require === "function" && require.m != null && require.c != null) {
|
||||||
wreqFallbackApplied = true;
|
const { stack } = new Error();
|
||||||
|
const webpackInstanceFileName = stack?.match(/\/assets\/(.+?\.js)/)?.[1];
|
||||||
|
|
||||||
// Make sure the require argument is actually the WebpackRequire function
|
logger.warn(
|
||||||
if (typeof require === "function" && require.m != null) {
|
"WebpackRequire was not initialized, falling back to WebpackRequire passed to the first called wrapped module factory (" +
|
||||||
const { stack } = new Error();
|
`id: ${String(module.id)}` + interpolateIfDefined`, WebpackInstance origin: ${webpackInstanceFileName}` +
|
||||||
const webpackInstanceFileName = stack?.match(/\/assets\/(.+?\.js)/)?.[1];
|
")"
|
||||||
|
);
|
||||||
|
|
||||||
logger.warn(
|
// Could technically be wrong, but it's better than nothing
|
||||||
"WebpackRequire was not initialized, falling back to WebpackRequire passed to the first called patched module factory (" +
|
_initWebpack(require as WebpackRequire);
|
||||||
`id: ${String(id)}` + interpolateIfDefined`, WebpackInstance origin: ${webpackInstanceFileName}` +
|
|
||||||
")"
|
|
||||||
);
|
|
||||||
|
|
||||||
_initWebpack(require as WebpackRequire);
|
|
||||||
} else if (IS_DEV) {
|
|
||||||
logger.error("WebpackRequire was not initialized, running modules without patches instead.");
|
|
||||||
return wrappedFactory[SYM_ORIGINAL_FACTORY].apply(this, args);
|
|
||||||
}
|
|
||||||
} else if (IS_DEV) {
|
} else if (IS_DEV) {
|
||||||
return wrappedFactory[SYM_ORIGINAL_FACTORY].apply(this, args);
|
logger.error("WebpackRequire was not initialized, running modules without patches instead.");
|
||||||
|
return originalFactory.apply(thisArg, argArray);
|
||||||
}
|
}
|
||||||
|
} else if (IS_DEV) {
|
||||||
|
return originalFactory.apply(thisArg, argArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let factoryReturn: unknown;
|
||||||
|
try {
|
||||||
|
factoryReturn = patchedFactory.apply(thisArg, argArray);
|
||||||
|
} catch (err) {
|
||||||
|
// Just re-throw Discord errors
|
||||||
|
if (patchedFactory === originalFactory) {
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
let factoryReturn: unknown;
|
logger.error("Error in patched module factory:\n", err);
|
||||||
try {
|
return originalFactory.apply(thisArg, argArray);
|
||||||
// Call the patched factory
|
}
|
||||||
factoryReturn = patchedFactory.apply(this, args);
|
|
||||||
} catch (err) {
|
|
||||||
// Just re-throw Discord errors
|
|
||||||
if (patchedFactory === wrappedFactory[SYM_ORIGINAL_FACTORY]) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.error("Error in patched module factory:\n", err);
|
exports = module.exports;
|
||||||
return wrappedFactory[SYM_ORIGINAL_FACTORY].apply(this, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
exports = module.exports;
|
if (typeof require === "function" && require.c) {
|
||||||
if (exports == null) {
|
if (_blacklistBadModules(require.c, exports, module.id)) {
|
||||||
return factoryReturn;
|
return factoryReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof require === "function") {
|
|
||||||
const shouldIgnoreModule = _shouldIgnoreModule(exports);
|
|
||||||
|
|
||||||
if (shouldIgnoreModule) {
|
|
||||||
if (require.c != null) {
|
|
||||||
Object.defineProperty(require.c, id, {
|
|
||||||
value: require.c[id],
|
|
||||||
enumerable: false,
|
|
||||||
configurable: true,
|
|
||||||
writable: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return factoryReturn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const callback of moduleListeners) {
|
|
||||||
try {
|
|
||||||
callback(exports, id);
|
|
||||||
} catch (err) {
|
|
||||||
logger.error("Error in Webpack module listener:\n", err, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [filter, callback] of waitForSubscriptions) {
|
|
||||||
try {
|
|
||||||
if (filter(exports)) {
|
|
||||||
waitForSubscriptions.delete(filter);
|
|
||||||
callback(exports, id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof exports !== "object") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const exportKey in exports) {
|
|
||||||
const exportValue = exports[exportKey];
|
|
||||||
|
|
||||||
if (exportValue != null && filter(exportValue)) {
|
|
||||||
waitForSubscriptions.delete(filter);
|
|
||||||
callback(exportValue, id);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
logger.error("Error while firing callback for Webpack waitFor subscription:\n", err, filter, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return factoryReturn;
|
|
||||||
};
|
|
||||||
|
|
||||||
wrappedFactory.toString = originalFactory.toString.bind(originalFactory);
|
|
||||||
wrappedFactory[SYM_ORIGINAL_FACTORY] = originalFactory;
|
|
||||||
|
|
||||||
if (IS_DEV && patchedFactory !== originalFactory) {
|
|
||||||
wrappedFactory[SYM_PATCHED_SOURCE] = patchedSource;
|
|
||||||
wrappedFactory[SYM_PATCHED_BY] = patchedBy;
|
|
||||||
originalFactory[SYM_PATCHED_SOURCE] = patchedSource;
|
|
||||||
originalFactory[SYM_PATCHED_BY] = patchedBy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error Allow GC to get into action, if possible
|
if (exports == null) {
|
||||||
originalFactory = undefined;
|
return factoryReturn;
|
||||||
return wrappedFactory;
|
}
|
||||||
|
|
||||||
|
for (const callback of moduleListeners) {
|
||||||
|
try {
|
||||||
|
callback(exports, module.id);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error("Error in Webpack module listener:\n", err, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [filter, callback] of waitForSubscriptions) {
|
||||||
|
try {
|
||||||
|
if (filter(exports)) {
|
||||||
|
waitForSubscriptions.delete(filter);
|
||||||
|
callback(exports, module.id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof exports !== "object") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const exportKey in exports) {
|
||||||
|
const exportValue = exports[exportKey];
|
||||||
|
|
||||||
|
if (exportValue != null && filter(exportValue)) {
|
||||||
|
waitForSubscriptions.delete(filter);
|
||||||
|
callback(exportValue, module.id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error("Error while firing callback for Webpack waitFor subscription:\n", err, filter, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return factoryReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Patches a module factory.
|
* Patches a module factory.
|
||||||
*
|
*
|
||||||
* @param id The id of the module
|
* @param moduleId The id of the module
|
||||||
* @param factory The original module factory
|
* @param originalFactory The original module factory
|
||||||
* @returns The patched module factory, the patched source of it, and the plugins that patched it
|
* @returns The patched module factory
|
||||||
*/
|
*/
|
||||||
function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFactory: AnyModuleFactory, patchedSource: string, patchedBy: Set<string>] {
|
function patchFactory(moduleId: PropertyKey, originalFactory: AnyModuleFactory): PatchedModuleFactory {
|
||||||
// 0, prefix to turn it into an expression: 0,function(){} would be invalid syntax without the 0,
|
// 0, prefix to turn it into an expression: 0,function(){} would be invalid syntax without the 0,
|
||||||
let code: string = "0," + String(factory);
|
let code: string = "0," + String(originalFactory);
|
||||||
let patchedSource = code;
|
let patchedSource = code;
|
||||||
let patchedFactory = factory;
|
let patchedFactory = originalFactory;
|
||||||
|
|
||||||
const patchedBy = new Set<string>();
|
const patchedBy = new Set<string>();
|
||||||
|
|
||||||
for (let i = 0; i < patches.length; i++) {
|
for (let i = 0; i < patches.length; i++) {
|
||||||
const patch = patches[i];
|
const patch = patches[i];
|
||||||
|
|
||||||
const moduleMatches = typeof patch.find === "string"
|
const buildNumber = getBuildNumber();
|
||||||
? code.includes(patch.find)
|
const shouldCheckBuildNumber = buildNumber !== -1;
|
||||||
: (patch.find.global && (patch.find.lastIndex = 0), patch.find.test(code));
|
|
||||||
|
|
||||||
if (!moduleMatches) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reporter eagerly patches and cannot retrieve the build number because this code runs before the module for it is loaded
|
|
||||||
const buildNumber = IS_REPORTER ? -1 : getBuildNumber();
|
|
||||||
const shouldCheckBuildNumber = !Settings.eagerPatches && buildNumber !== -1;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
shouldCheckBuildNumber &&
|
shouldCheckBuildNumber &&
|
||||||
(patch.fromBuild != null && buildNumber < patch.fromBuild) ||
|
(patch.fromBuild != null && buildNumber < patch.fromBuild) ||
|
||||||
(patch.toBuild != null && buildNumber > patch.toBuild)
|
(patch.toBuild != null && buildNumber > patch.toBuild)
|
||||||
) {
|
) {
|
||||||
|
patches.splice(i--, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleMatches = typeof patch.find === "string"
|
||||||
|
? code.includes(patch.find)
|
||||||
|
: (patch.find.global && (patch.find.lastIndex = 0), patch.find.test(code));
|
||||||
|
|
||||||
|
if (!moduleMatches) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,7 +505,7 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto
|
||||||
});
|
});
|
||||||
|
|
||||||
const previousCode = code;
|
const previousCode = code;
|
||||||
const previousFactory = factory;
|
const previousFactory = originalFactory;
|
||||||
let markedAsPatched = false;
|
let markedAsPatched = false;
|
||||||
|
|
||||||
// We change all patch.replacement to array in plugins/index
|
// We change all patch.replacement to array in plugins/index
|
||||||
|
@ -482,18 +524,18 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastCode = code;
|
const lastCode = code;
|
||||||
const lastFactory = factory;
|
const lastFactory = originalFactory;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [newCode, totalTime] = executePatch(replacement.match, replacement.replace as string);
|
const [newCode, totalTime] = executePatch(replacement.match, replacement.replace as string);
|
||||||
|
|
||||||
if (IS_REPORTER) {
|
if (IS_REPORTER) {
|
||||||
patchTimings.push([patch.plugin, id, replacement.match, totalTime]);
|
patchTimings.push([patch.plugin, moduleId, replacement.match, totalTime]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newCode === code) {
|
if (newCode === code) {
|
||||||
if (!patch.noWarn) {
|
if (!(patch.noWarn || replacement.noWarn)) {
|
||||||
logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${String(id)}): ${replacement.match}`);
|
logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${String(moduleId)}): ${replacement.match}`);
|
||||||
if (IS_DEV) {
|
if (IS_DEV) {
|
||||||
logger.debug("Function Source:\n", code);
|
logger.debug("Function Source:\n", code);
|
||||||
}
|
}
|
||||||
|
@ -514,8 +556,13 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pluginsList = [...patchedBy];
|
||||||
|
if (!patchedBy.has(patch.plugin)) {
|
||||||
|
pluginsList.push(patch.plugin);
|
||||||
|
}
|
||||||
|
|
||||||
code = newCode;
|
code = newCode;
|
||||||
patchedSource = `// Webpack Module ${String(id)} - Patched by ${[...patchedBy, patch.plugin].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${String(id)}`;
|
patchedSource = `// Webpack Module ${String(moduleId)} - Patched by ${pluginsList.join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${String(moduleId)}`;
|
||||||
patchedFactory = (0, eval)(patchedSource);
|
patchedFactory = (0, eval)(patchedSource);
|
||||||
|
|
||||||
if (!patchedBy.has(patch.plugin)) {
|
if (!patchedBy.has(patch.plugin)) {
|
||||||
|
@ -523,7 +570,7 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto
|
||||||
markedAsPatched = true;
|
markedAsPatched = true;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(`Patch by ${patch.plugin} errored (Module id is ${String(id)}): ${replacement.match}\n`, err);
|
logger.error(`Patch by ${patch.plugin} errored (Module id is ${String(moduleId)}): ${replacement.match}\n`, err);
|
||||||
|
|
||||||
if (IS_DEV) {
|
if (IS_DEV) {
|
||||||
diffErroredPatch(code, lastCode, lastCode.match(replacement.match)!);
|
diffErroredPatch(code, lastCode, lastCode.match(replacement.match)!);
|
||||||
|
@ -550,7 +597,14 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [patchedFactory, patchedSource, patchedBy];
|
patchedFactory[SYM_ORIGINAL_FACTORY] = originalFactory;
|
||||||
|
|
||||||
|
if (IS_DEV && patchedFactory !== originalFactory) {
|
||||||
|
originalFactory[SYM_PATCHED_SOURCE] = patchedSource;
|
||||||
|
originalFactory[SYM_PATCHED_BY] = patchedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
return patchedFactory as PatchedModuleFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
function diffErroredPatch(code: string, lastCode: string, match: RegExpMatchArray) {
|
function diffErroredPatch(code: string, lastCode: string, match: RegExpMatchArray) {
|
||||||
|
|
|
@ -22,7 +22,8 @@ import { Logger } from "@utils/Logger";
|
||||||
import { canonicalizeMatch } from "@utils/patches";
|
import { canonicalizeMatch } from "@utils/patches";
|
||||||
|
|
||||||
import { traceFunction } from "../debug/Tracer";
|
import { traceFunction } from "../debug/Tracer";
|
||||||
import { AnyModuleFactory, ModuleExports, WebpackRequire } from "./wreq";
|
import { Flux } from "./common";
|
||||||
|
import { AnyModuleFactory, AnyWebpackRequire, ModuleExports, WebpackRequire } from "./wreq";
|
||||||
|
|
||||||
const logger = new Logger("Webpack");
|
const logger = new Logger("Webpack");
|
||||||
|
|
||||||
|
@ -90,17 +91,13 @@ export const filters = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CallbackFn = (module: ModuleExports, id: PropertyKey) => void;
|
export type CallbackFn = (module: ModuleExports, id: PropertyKey) => void;
|
||||||
export type FactoryListernFn = (factory: AnyModuleFactory) => void;
|
export type FactoryListernFn = (factory: AnyModuleFactory, moduleId: PropertyKey) => void;
|
||||||
|
|
||||||
export const waitForSubscriptions = new Map<FilterFn, CallbackFn>();
|
export const waitForSubscriptions = new Map<FilterFn, CallbackFn>();
|
||||||
export const moduleListeners = new Set<CallbackFn>();
|
export const moduleListeners = new Set<CallbackFn>();
|
||||||
export const factoryListeners = new Set<FactoryListernFn>();
|
export const factoryListeners = new Set<FactoryListernFn>();
|
||||||
|
|
||||||
export function _initWebpack(webpackRequire: WebpackRequire) {
|
export function _initWebpack(webpackRequire: WebpackRequire) {
|
||||||
if (webpackRequire.c == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wreq = webpackRequire;
|
wreq = webpackRequire;
|
||||||
cache = webpackRequire.c;
|
cache = webpackRequire.c;
|
||||||
|
|
||||||
|
@ -115,18 +112,38 @@ export function _initWebpack(webpackRequire: WebpackRequire) {
|
||||||
// Credits to Zerebos for implementing this in BD, thus giving the idea for us to implement it too
|
// Credits to Zerebos for implementing this in BD, thus giving the idea for us to implement it too
|
||||||
const TypedArray = Object.getPrototypeOf(Int8Array);
|
const TypedArray = Object.getPrototypeOf(Int8Array);
|
||||||
|
|
||||||
function _shouldIgnoreValue(value: any) {
|
const PROXY_CHECK = "is this a proxy that returns values for any key?";
|
||||||
|
function shouldIgnoreValue(value: any) {
|
||||||
if (value == null) return true;
|
if (value == null) return true;
|
||||||
if (value === window) return true;
|
if (value === window) return true;
|
||||||
if (value === document || value === document.documentElement) return true;
|
if (value === document || value === document.documentElement) return true;
|
||||||
if (value[Symbol.toStringTag] === "DOMTokenList") return true;
|
if (value[Symbol.toStringTag] === "DOMTokenList" || value[Symbol.toStringTag] === "IntlMessagesProxy") return true;
|
||||||
|
// Discord might export a Proxy that returns non-null values for any property key which would pass all findByProps filters.
|
||||||
|
// One example of this is their i18n Proxy. However, that is already covered by the IntlMessagesProxy check above.
|
||||||
|
// As a fallback if they ever change the name or add a new Proxy, use a unique string to detect such proxies and ignore them
|
||||||
|
if (value[PROXY_CHECK] !== void 0) {
|
||||||
|
// their i18n Proxy "caches" by setting each accessed property to the return, so try to delete
|
||||||
|
Reflect.deleteProperty(value, PROXY_CHECK);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (value instanceof TypedArray) return true;
|
if (value instanceof TypedArray) return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function _shouldIgnoreModule(exports: any) {
|
function makePropertyNonEnumerable(target: Record<PropertyKey, any>, key: PropertyKey) {
|
||||||
if (_shouldIgnoreValue(exports)) {
|
const descriptor = Object.getOwnPropertyDescriptor(target, key);
|
||||||
|
if (descriptor == null) return;
|
||||||
|
|
||||||
|
Reflect.defineProperty(target, key, {
|
||||||
|
...descriptor,
|
||||||
|
enumerable: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function _blacklistBadModules(requireCache: NonNullable<AnyWebpackRequire["c"]>, exports: ModuleExports, moduleId: PropertyKey) {
|
||||||
|
if (shouldIgnoreValue(exports)) {
|
||||||
|
makePropertyNonEnumerable(requireCache, moduleId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,14 +151,16 @@ export function _shouldIgnoreModule(exports: any) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let allNonEnumerable = true;
|
let hasOnlyBadProperties = true;
|
||||||
for (const exportKey in exports) {
|
for (const exportKey in exports) {
|
||||||
if (!_shouldIgnoreValue(exports[exportKey])) {
|
if (shouldIgnoreValue(exports[exportKey])) {
|
||||||
allNonEnumerable = false;
|
makePropertyNonEnumerable(exports, exportKey);
|
||||||
|
} else {
|
||||||
|
hasOnlyBadProperties = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return allNonEnumerable;
|
return hasOnlyBadProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
let devToolsOpen = false;
|
let devToolsOpen = false;
|
||||||
|
@ -409,7 +428,10 @@ export function findByCodeLazy(...code: CodeFilter) {
|
||||||
* Find a store by its displayName
|
* Find a store by its displayName
|
||||||
*/
|
*/
|
||||||
export function findStore(name: StoreNameFilter) {
|
export function findStore(name: StoreNameFilter) {
|
||||||
const res = find(filters.byStoreName(name), { isIndirect: true });
|
const res = Flux.Store.getAll
|
||||||
|
? Flux.Store.getAll().find(filters.byStoreName(name))
|
||||||
|
: find(filters.byStoreName(name), { isIndirect: true });
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
handleModuleNotFound("findStore", name);
|
handleModuleNotFound("findStore", name);
|
||||||
return res;
|
return res;
|
||||||
|
@ -477,12 +499,27 @@ export function findExportedComponentLazy<T extends object = any>(...props: Prop
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAllPropertyNames(object: Record<PropertyKey, any>, includeNonEnumerable: boolean) {
|
||||||
|
const names = new Set<PropertyKey>();
|
||||||
|
|
||||||
|
const getKeys = includeNonEnumerable ? Object.getOwnPropertyNames : Object.keys;
|
||||||
|
do {
|
||||||
|
getKeys(object).forEach(name => name !== "__esModule" && names.add(name));
|
||||||
|
object = Object.getPrototypeOf(object);
|
||||||
|
} while (object != null);
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module)
|
* Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module)
|
||||||
* then maps it into an easily usable module via the specified mappers.
|
* then maps it into an easily usable module via the specified mappers.
|
||||||
*
|
*
|
||||||
* @param code The code to look for
|
* @param code The code to look for
|
||||||
* @param mappers Mappers to create the non mangled exports
|
* @param mappers Mappers to create the non mangled exports
|
||||||
|
* @param includeBlacklistedExports Whether to include blacklisted exports in the search.
|
||||||
|
* These exports are dangerous. Accessing properties on them may throw errors
|
||||||
|
* or always return values (so a byProps filter will always return true)
|
||||||
* @returns Unmangled exports as specified in mappers
|
* @returns Unmangled exports as specified in mappers
|
||||||
*
|
*
|
||||||
* @example mapMangledModule("headerIdIsManaged:", {
|
* @example mapMangledModule("headerIdIsManaged:", {
|
||||||
|
@ -490,7 +527,7 @@ export function findExportedComponentLazy<T extends object = any>(...props: Prop
|
||||||
* closeModal: filters.byCode("key==")
|
* closeModal: filters.byCode("key==")
|
||||||
* })
|
* })
|
||||||
*/
|
*/
|
||||||
export const mapMangledModule = traceFunction("mapMangledModule", function mapMangledModule<S extends string>(code: string | RegExp | CodeFilter, mappers: Record<S, FilterFn>): Record<S, any> {
|
export const mapMangledModule = traceFunction("mapMangledModule", function mapMangledModule<S extends string>(code: string | RegExp | CodeFilter, mappers: Record<S, FilterFn>, includeBlacklistedExports = false): Record<S, any> {
|
||||||
const exports = {} as Record<S, any>;
|
const exports = {} as Record<S, any>;
|
||||||
|
|
||||||
const id = findModuleId(...Array.isArray(code) ? code : [code]);
|
const id = findModuleId(...Array.isArray(code) ? code : [code]);
|
||||||
|
@ -498,8 +535,9 @@ export const mapMangledModule = traceFunction("mapMangledModule", function mapMa
|
||||||
return exports;
|
return exports;
|
||||||
|
|
||||||
const mod = wreq(id as any);
|
const mod = wreq(id as any);
|
||||||
|
const keys = getAllPropertyNames(mod, includeBlacklistedExports);
|
||||||
outer:
|
outer:
|
||||||
for (const key in mod) {
|
for (const key of keys) {
|
||||||
const member = mod[key];
|
const member = mod[key];
|
||||||
for (const newName in mappers) {
|
for (const newName in mappers) {
|
||||||
// if the current mapper matches this module
|
// if the current mapper matches this module
|
||||||
|
@ -513,24 +551,13 @@ export const mapMangledModule = traceFunction("mapMangledModule", function mapMa
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link mapMangledModule}, lazy.
|
* lazy mapMangledModule
|
||||||
|
* @see {@link mapMangledModule}
|
||||||
* Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module)
|
|
||||||
* then maps it into an easily usable module via the specified mappers.
|
|
||||||
*
|
|
||||||
* @param code The code to look for
|
|
||||||
* @param mappers Mappers to create the non mangled exports
|
|
||||||
* @returns Unmangled exports as specified in mappers
|
|
||||||
*
|
|
||||||
* @example mapMangledModule("headerIdIsManaged:", {
|
|
||||||
* openModal: filters.byCode("headerIdIsManaged:"),
|
|
||||||
* closeModal: filters.byCode("key==")
|
|
||||||
* })
|
|
||||||
*/
|
*/
|
||||||
export function mapMangledModuleLazy<S extends string>(code: string | RegExp | CodeFilter, mappers: Record<S, FilterFn>): Record<S, any> {
|
export function mapMangledModuleLazy<S extends string>(code: string | RegExp | CodeFilter, mappers: Record<S, FilterFn>, includeBlacklistedExports = false): Record<S, any> {
|
||||||
if (IS_REPORTER) lazyWebpackSearchHistory.push(["mapMangledModule", [code, mappers]]);
|
if (IS_REPORTER) lazyWebpackSearchHistory.push(["mapMangledModule", [code, mappers, includeBlacklistedExports]]);
|
||||||
|
|
||||||
return proxyLazy(() => mapMangledModule(code, mappers));
|
return proxyLazy(() => mapMangledModule(code, mappers, includeBlacklistedExports));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DefaultExtractAndLoadChunksRegex = /(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/;
|
export const DefaultExtractAndLoadChunksRegex = /(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/;
|
||||||
|
|
93
src/webpack/wreq.d.ts
vendored
93
src/webpack/wreq.d.ts
vendored
|
@ -17,14 +17,11 @@ export type Module = {
|
||||||
/** exports can be anything, however initially it is always an empty object */
|
/** exports can be anything, however initially it is always an empty object */
|
||||||
export type ModuleFactory = (this: ModuleExports, module: Module, exports: ModuleExports, require: WebpackRequire) => void;
|
export type ModuleFactory = (this: ModuleExports, module: Module, exports: ModuleExports, require: WebpackRequire) => void;
|
||||||
|
|
||||||
export type WebpackQueues = unique symbol | "__webpack_queues__";
|
/** Keys here can be symbols too, but we can't properly type them */
|
||||||
export type WebpackExports = unique symbol | "__webpack_exports__";
|
|
||||||
export type WebpackError = unique symbol | "__webpack_error__";
|
|
||||||
|
|
||||||
export type AsyncModulePromise = Promise<ModuleExports> & {
|
export type AsyncModulePromise = Promise<ModuleExports> & {
|
||||||
[WebpackQueues]: (fnQueue: ((queue: any[]) => any)) => any;
|
"__webpack_queues__": (fnQueue: ((queue: any[]) => any)) => any;
|
||||||
[WebpackExports]: ModuleExports;
|
"__webpack_exports__": ModuleExports;
|
||||||
[WebpackError]?: any;
|
"__webpack_error__"?: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AsyncModuleBody = (
|
export type AsyncModuleBody = (
|
||||||
|
@ -33,27 +30,45 @@ export type AsyncModuleBody = (
|
||||||
asyncResult: (error?: any) => void
|
asyncResult: (error?: any) => void
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
|
|
||||||
export type ChunkHandlers = {
|
export type EnsureChunkHandlers = {
|
||||||
/**
|
/**
|
||||||
* Ensures the js file for this chunk is loaded, or starts to load if it's not.
|
* Ensures the js file for this chunk is loaded, or starts to load if it's not.
|
||||||
* @param chunkId The chunk id
|
* @param chunkId The chunk id
|
||||||
* @param promises The promises array to add the loading promise to
|
* @param promises The promises array to add the loading promise to
|
||||||
*/
|
*/
|
||||||
j: (this: ChunkHandlers, chunkId: PropertyKey, promises: Promise<void[]>) => void,
|
j: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise<void[]>) => void;
|
||||||
/**
|
/**
|
||||||
* Ensures the css file for this chunk is loaded, or starts to load if it's not.
|
* Ensures the css file for this chunk is loaded, or starts to load if it's not.
|
||||||
* @param chunkId The chunk id
|
* @param chunkId The chunk id
|
||||||
* @param promises The promises array to add the loading promise to. This array will likely contain the promise of the js file too
|
* @param promises The promises array to add the loading promise to. This array will likely contain the promise of the js file too
|
||||||
*/
|
*/
|
||||||
css: (this: ChunkHandlers, chunkId: PropertyKey, promises: Promise<void[]>) => void,
|
css: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise<void[]>) => void;
|
||||||
|
/**
|
||||||
|
* Trigger for prefetching next chunks. This is called after ensuring a chunk is loaded and internally looks up
|
||||||
|
* a map to see if the chunk that just loaded has next chunks to prefetch.
|
||||||
|
*
|
||||||
|
* Note that this does not add an extra promise to the promises array, and instead only executes the prefetching after
|
||||||
|
* calling Promise.all on the promises array.
|
||||||
|
* @param chunkId The chunk id
|
||||||
|
* @param promises The promises array of ensuring the chunk is loaded
|
||||||
|
*/
|
||||||
|
prefetch: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise<void[]>) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PrefetchChunkHandlers = {
|
||||||
|
/**
|
||||||
|
* Prefetches the js file for this chunk.
|
||||||
|
* @param chunkId The chunk id
|
||||||
|
*/
|
||||||
|
j: (this: PrefetchChunkHandlers, chunkId: PropertyKey) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ScriptLoadDone = (event: Event) => void;
|
export type ScriptLoadDone = (event: Event) => void;
|
||||||
|
|
||||||
// export type OnChunksLoaded = ((this: WebpackRequire, result: any, chunkIds: PropertyKey[] | undefined | null, callback: () => any, priority: number) => any) & {
|
export type OnChunksLoaded = ((this: WebpackRequire, result: any, chunkIds: PropertyKey[] | undefined | null, callback: () => any, priority: number) => any) & {
|
||||||
// /** Check if a chunk has been loaded */
|
/** Check if a chunk has been loaded */
|
||||||
// j: (this: OnChunksLoaded, chunkId: PropertyKey) => boolean;
|
j: (this: OnChunksLoaded, chunkId: PropertyKey) => boolean;
|
||||||
// };
|
};
|
||||||
|
|
||||||
export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
|
export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
|
||||||
/** The module factories, where all modules that have been loaded are stored (pre-loaded or loaded by lazy chunks) */
|
/** The module factories, where all modules that have been loaded are stored (pre-loaded or loaded by lazy chunks) */
|
||||||
|
@ -134,14 +149,21 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
|
||||||
* }
|
* }
|
||||||
* // exports is now { exportName: someExportedValue } (but each value is actually a getter)
|
* // exports is now { exportName: someExportedValue } (but each value is actually a getter)
|
||||||
*/
|
*/
|
||||||
d: (this: WebpackRequire, exports: AnyRecord, definiton: AnyRecord) => void;
|
d: (this: WebpackRequire, exports: Record<PropertyKey, any>, definiton: Record<PropertyKey, () => ModuleExports>) => void;
|
||||||
/** The chunk handlers, which are used to ensure the files of the chunks are loaded, or load if necessary */
|
/** The ensure chunk handlers, which are used to ensure the files of the chunks are loaded, or load if necessary */
|
||||||
f: ChunkHandlers;
|
f: EnsureChunkHandlers;
|
||||||
/**
|
/**
|
||||||
* The ensure chunk function, it ensures a chunk is loaded, or loads if needed.
|
* The ensure chunk function, it ensures a chunk is loaded, or loads if needed.
|
||||||
* Internally it uses the handlers in {@link WebpackRequire.f} to load/ensure the chunk is loaded.
|
* Internally it uses the handlers in {@link WebpackRequire.f} to load/ensure the chunk is loaded.
|
||||||
*/
|
*/
|
||||||
e: (this: WebpackRequire, chunkId: PropertyKey) => Promise<void[]>;
|
e: (this: WebpackRequire, chunkId: PropertyKey) => Promise<void[]>;
|
||||||
|
/** The prefetch chunk handlers, which are used to prefetch the files of the chunks */
|
||||||
|
F: PrefetchChunkHandlers;
|
||||||
|
/**
|
||||||
|
* The prefetch chunk function.
|
||||||
|
* Internally it uses the handlers in {@link WebpackRequire.F} to prefetch a chunk.
|
||||||
|
*/
|
||||||
|
E: (this: WebpackRequire, chunkId: PropertyKey) => void;
|
||||||
/** Get the filename for the css part of a chunk */
|
/** Get the filename for the css part of a chunk */
|
||||||
k: (this: WebpackRequire, chunkId: PropertyKey) => string;
|
k: (this: WebpackRequire, chunkId: PropertyKey) => string;
|
||||||
/** Get the filename for the js part of a chunk */
|
/** Get the filename for the js part of a chunk */
|
||||||
|
@ -162,18 +184,18 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
|
||||||
r: (this: WebpackRequire, exports: ModuleExports) => void;
|
r: (this: WebpackRequire, exports: ModuleExports) => void;
|
||||||
/** Node.js module decorator. Decorates a module as a Node.js module */
|
/** Node.js module decorator. Decorates a module as a Node.js module */
|
||||||
nmd: (this: WebpackRequire, module: Module) => any;
|
nmd: (this: WebpackRequire, module: Module) => any;
|
||||||
// /**
|
/**
|
||||||
// * Register deferred code which will be executed when the passed chunks are loaded.
|
* Register deferred code which will be executed when the passed chunks are loaded.
|
||||||
// *
|
*
|
||||||
// * If chunkIds is defined, it defers the execution of the callback and returns undefined.
|
* If chunkIds is defined, it defers the execution of the callback and returns undefined.
|
||||||
// *
|
*
|
||||||
// * If chunkIds is undefined, and no deferred code exists or can be executed, it returns the value of the result argument.
|
* If chunkIds is undefined, and no deferred code exists or can be executed, it returns the value of the result argument.
|
||||||
// *
|
*
|
||||||
// * If chunkIds is undefined, and some deferred code can already be executed, it returns the result of the callback function of the last deferred code.
|
* If chunkIds is undefined, and some deferred code can already be executed, it returns the result of the callback function of the last deferred code.
|
||||||
// *
|
*
|
||||||
// * When (priority & 1) it will wait for all other handlers with lower priority to be executed before itself is executed.
|
* When (priority & 1) it will wait for all other handlers with lower priority to be executed before itself is executed.
|
||||||
// */
|
*/
|
||||||
// O: OnChunksLoaded;
|
O: OnChunksLoaded;
|
||||||
/**
|
/**
|
||||||
* Instantiate a wasm instance with source using "wasmModuleHash", and importObject "importsObj", and then assign the exports of its instance to "exports".
|
* Instantiate a wasm instance with source using "wasmModuleHash", and importObject "importsObj", and then assign the exports of its instance to "exports".
|
||||||
* @returns The exports argument, but now assigned with the exports of the wasm instance
|
* @returns The exports argument, but now assigned with the exports of the wasm instance
|
||||||
|
@ -185,6 +207,13 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
|
||||||
j: string;
|
j: string;
|
||||||
/** Document baseURI or WebWorker location.href */
|
/** Document baseURI or WebWorker location.href */
|
||||||
b: string;
|
b: string;
|
||||||
|
|
||||||
|
/* rspack only */
|
||||||
|
|
||||||
|
/** rspack version */
|
||||||
|
rv: (this: WebpackRequire) => string;
|
||||||
|
/** rspack unique id */
|
||||||
|
ruid: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Utility section for Vencord
|
// Utility section for Vencord
|
||||||
|
@ -200,12 +229,10 @@ export type AnyModuleFactory = ((this: ModuleExports, module: Module, exports: M
|
||||||
[SYM_PATCHED_BY]?: Set<string>;
|
[SYM_PATCHED_BY]?: Set<string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WrappedModuleFactory = AnyModuleFactory & {
|
export type PatchedModuleFactory = AnyModuleFactory & {
|
||||||
[SYM_ORIGINAL_FACTORY]: AnyModuleFactory;
|
[SYM_ORIGINAL_FACTORY]: AnyModuleFactory;
|
||||||
[SYM_PATCHED_SOURCE]?: string;
|
[SYM_PATCHED_SOURCE]?: string;
|
||||||
[SYM_PATCHED_BY]?: Set<string>;
|
[SYM_PATCHED_BY]?: Set<string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MaybeWrappedModuleFactory = AnyModuleFactory | WrappedModuleFactory;
|
export type MaybePatchedModuleFactory = PatchedModuleFactory | AnyModuleFactory;
|
||||||
|
|
||||||
export type WrappedModuleFactories = Record<PropertyKey, WrappedModuleFactory>;
|
|
||||||
|
|
|
@ -29,7 +29,9 @@
|
||||||
"@shared/*": ["./shared/*"],
|
"@shared/*": ["./shared/*"],
|
||||||
"@webpack/types": ["./webpack/common/types"],
|
"@webpack/types": ["./webpack/common/types"],
|
||||||
"@webpack/common": ["./webpack/common"],
|
"@webpack/common": ["./webpack/common"],
|
||||||
"@webpack": ["./webpack/webpack"]
|
"@webpack": ["./webpack/webpack"],
|
||||||
|
"@webpack/patcher": ["./webpack/patchWebpack"],
|
||||||
|
"@webpack/wreq.d": ["./webpack/wreq.d"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"plugins": [
|
"plugins": [
|
||||||
|
|
Loading…
Add table
Reference in a new issue