Compare commits

...

29 commits

Author SHA1 Message Date
Vendicated 7781dface9
fix: apply update only on quit 2024-07-21 02:52:45 +02:00
Vendicated f9fb3bbba7
fix watch erroring when clean building 2024-07-21 01:29:46 +02:00
Vendicated 8c5217f9f2
require node>=20 2024-07-21 00:54:41 +02:00
Vendicated 5fab0207fa
do not the version 2024-07-19 23:05:31 +02:00
Vendicated 0cf6542f3c
delete .map files 2024-07-19 23:03:49 +02:00
Vendicated f4c19705d7
ignore 0 byte files 2024-07-19 22:57:40 +02:00
Vendicated 9f5dee00d4
workflow: run on any repo 2024-07-19 22:53:46 +02:00
Vendicated 63b359d970
version 2024-07-19 22:51:29 +02:00
Vendicated 090a9f5caf
don't use asar in dev 2024-07-19 22:02:17 +02:00
Vendicated d48f52e8c3
remove obsolete workaround 2024-07-19 21:34:39 +02:00
Vendicated 7aeb884390
i forgot what i changed but im committing anyway 2024-07-19 21:32:10 +02:00
Vendicated 44cd30b23a
fix types 2024-07-19 20:45:01 +02:00
Vendicated 22fdde6e39
add error handling back 2024-07-19 20:43:23 +02:00
Vendicated 8fa7d006a4
update build scripts to latest esbuild & typescript 2024-07-19 20:40:24 +02:00
Vendicated d84943a6d7
no balls :/ 2024-07-19 03:29:54 +02:00
Vendicated 839b62c2d9
update runInstaller 2024-07-19 03:29:44 +02:00
Vendicated 445dfce4a8
fix css watch 2024-07-19 02:57:11 +02:00
Vendicated 65e91cf22e
omg i love when vscode gets stuck on saving 2024-07-19 02:51:52 +02:00
Vendicated b91cb742b1
update outdated paths 2024-07-19 02:50:24 +02:00
Vendicated 611b94b6c7
use exit instead of quit 2024-07-19 01:43:26 +02:00
Vendicated ffb73107e6
flatpak explosion 2024-07-19 01:21:11 +02:00
Vendicated d9c469755b
fixes 2024-07-19 01:20:09 +02:00
Vendicated fbfbe33c0a
j 2024-07-19 00:53:40 +02:00
Vendicated 2ace675c00
migrate legacy installs 2024-07-19 00:52:32 +02:00
Vendicated 7148c29ed1
more clean 2024-07-18 21:52:31 +02:00
Vendicated 5797506569
add dev build workaround 2024-07-18 21:50:12 +02:00
Vendicated e119f092bf
nyaa 2024-07-18 04:37:44 +02:00
Vendicated e8910615a7
bleh 2024-07-18 04:35:51 +02:00
Vendicated 6cf2e0c2a5
[WIP] package vencord as asar 2024-07-18 04:34:09 +02:00
18 changed files with 842 additions and 441 deletions

View file

@ -40,9 +40,28 @@ jobs:
- name: Generate plugin list - name: Generate plugin list
run: pnpm generatePluginJson dist/plugins.json dist/plugin-readmes.json run: pnpm generatePluginJson dist/plugins.json dist/plugin-readmes.json
- name: Clean up obsolete files - name: Collect files to be released
run: | run: |
rm -rf dist/*-unpacked dist/monaco Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map cd dist
mkdir release
cp browser/browser.* release
cp Vencord.user.{js,js.LEGAL.txt} release
# copy the plugin data jsons, the extension zips and the desktop/vesktop asars
cp *.{json,zip,asar} release
# legacy un-asared files
# FIXME: remove at some point
cp desktop/* release
for file in vesktop/*; do
filename=$(basename "$file")
cp "$file" "release/vencordDesktop${filename^}"
done
find release -size 0 -delete
rm release/package.json
rm release/*.map
- name: Get some values needed for the release - name: Get some values needed for the release
id: release_values id: release_values
@ -50,16 +69,14 @@ jobs:
echo "release_tag=$(git rev-parse --short HEAD)" >> $GITHUB_ENV echo "release_tag=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
- name: Upload DevBuild as release - name: Upload DevBuild as release
if: github.repository == 'Vendicated/Vencord'
run: | run: |
gh release upload devbuild --clobber dist/* gh release upload devbuild --clobber dist/release/*
gh release edit devbuild --title "DevBuild $RELEASE_TAG" gh release edit devbuild --title "DevBuild $RELEASE_TAG"
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ env.release_tag }} RELEASE_TAG: ${{ env.release_tag }}
- name: Upload DevBuild to builds repo - name: Upload DevBuild to builds repo
if: github.repository == 'Vendicated/Vencord'
run: | run: |
git config --global user.name "$USERNAME" git config --global user.name "$USERNAME"
git config --global user.email actions@github.com git config --global user.email actions@github.com
@ -69,7 +86,7 @@ jobs:
GLOBIGNORE=.git:.gitignore:README.md:LICENSE GLOBIGNORE=.git:.gitignore:README.md:LICENSE
rm -rf * rm -rf *
cp -r ../dist/* . cp -r ../dist/release/* .
git add -A git add -A
git commit -m "Builds for https://github.com/$GITHUB_REPOSITORY/commit/$GITHUB_SHA" git commit -m "Builds for https://github.com/$GITHUB_REPOSITORY/commit/$GITHUB_SHA"

View file

@ -22,7 +22,7 @@ jobs:
- uses: pnpm/action-setup@v3 # Install pnpm using packageManager key in package.json - uses: pnpm/action-setup@v3 # Install pnpm using packageManager key in package.json
- name: Use Node.js 19 - name: Use Node.js 20
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 20 node-version: 20
@ -36,7 +36,7 @@ jobs:
- name: Publish extension - name: Publish extension
run: | run: |
cd dist/chromium-unpacked cd dist/browser/chromium-unpacked
pnpx chrome-webstore-upload-cli@2.1.0 upload --auto-publish pnpx chrome-webstore-upload-cli@2.1.0 upload --auto-publish
env: env:
EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }} EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}

View file

@ -14,9 +14,9 @@
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"author": "Vendicated", "author": "Vendicated",
"scripts": { "scripts": {
"build": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/build.mjs", "build": "tsx scripts/build/build.mts",
"buildStandalone": "pnpm build --standalone", "buildStandalone": "pnpm build --standalone",
"buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs", "buildWeb": "tsx scripts/build/buildWeb.mts",
"buildWebStandalone": "pnpm buildWeb --standalone", "buildWebStandalone": "pnpm buildWeb --standalone",
"buildReporter": "pnpm buildWebStandalone --reporter --skip-extension", "buildReporter": "pnpm buildWebStandalone --reporter --skip-extension",
"buildReporterDesktop": "pnpm build --reporter", "buildReporterDesktop": "pnpm build --reporter",
@ -46,6 +46,7 @@
"virtual-merge": "^1.0.1" "virtual-merge": "^1.0.1"
}, },
"devDependencies": { "devDependencies": {
"@electron/asar": "^3.2.10",
"@types/chrome": "^0.0.246", "@types/chrome": "^0.0.246",
"@types/diff": "^5.0.3", "@types/diff": "^5.0.3",
"@types/lodash": "^4.14.194", "@types/lodash": "^4.14.194",
@ -57,7 +58,7 @@
"@typescript-eslint/parser": "^5.59.1", "@typescript-eslint/parser": "^5.59.1",
"diff": "^5.1.0", "diff": "^5.1.0",
"discord-types": "^1.3.26", "discord-types": "^1.3.26",
"esbuild": "^0.15.18", "esbuild": "^0.23.0",
"eslint": "^8.46.0", "eslint": "^8.46.0",
"eslint-import-resolver-alias": "^1.1.2", "eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-path-alias": "^1.0.0", "eslint-plugin-path-alias": "^1.0.0",
@ -71,7 +72,7 @@
"stylelint": "^15.6.0", "stylelint": "^15.6.0",
"stylelint-config-standard": "^33.0.0", "stylelint-config-standard": "^33.0.0",
"ts-patch": "^3.1.2", "ts-patch": "^3.1.2",
"tsx": "^3.12.7", "tsx": "^4.16.2",
"type-fest": "^3.9.0", "type-fest": "^3.9.0",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"typescript-transform-paths": "^3.4.7", "typescript-transform-paths": "^3.4.7",
@ -104,7 +105,7 @@
"sourceDir": "./dist/firefox-unpacked" "sourceDir": "./dist/firefox-unpacked"
}, },
"engines": { "engines": {
"node": ">=18", "node": ">=20",
"pnpm": ">=9" "pnpm": ">=9"
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -17,31 +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"; import { createPackage } from "@electron/asar";
import { readdir } from "fs/promises"; import { BuildOptions, Plugin } from "esbuild";
import { existsSync, readdirSync } from "fs";
import { readdir, rm, writeFile } 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, watch } from "./common.mjs"; import { addBuild, BUILD_TIMESTAMP, buildOrWatchAll, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_STANDALONE, IS_UPDATER_DISABLED, resolvePluginName, VERSION, watch } from "./common.mjs";
const defines = { const defines = {
IS_STANDALONE, IS_STANDALONE: String(IS_STANDALONE),
IS_DEV, IS_DEV: String(IS_DEV),
IS_REPORTER, IS_REPORTER: String(IS_REPORTER),
IS_UPDATER_DISABLED, IS_UPDATER_DISABLED: String(IS_UPDATER_DISABLED),
IS_WEB: false, IS_WEB: "false",
IS_EXTENSION: false, IS_EXTENSION: "false",
VERSION: JSON.stringify(VERSION), VERSION: JSON.stringify(VERSION),
BUILD_TIMESTAMP BUILD_TIMESTAMP: String(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}
*/
const nodeCommonOpts = { const nodeCommonOpts = {
...commonOpts, ...commonOpts,
format: "cjs", format: "cjs",
@ -49,15 +48,12 @@ const nodeCommonOpts = {
target: ["esnext"], target: ["esnext"],
external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external], external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external],
define: defines define: defines
}; } satisfies BuildOptions;
const sourceMapFooter = s => watch ? "" : `//# sourceMappingURL=vencord://${s}.js.map`; const sourceMapFooter = (s: string) => watch ? "" : `//# sourceMappingURL=vencord://${s}.js.map`;
const sourcemap = watch ? "inline" : "external"; const sourcemap = watch ? "inline" : "external";
/** const globNativesPlugin: Plugin = {
* @type {import("esbuild").Plugin}
*/
const globNativesPlugin = {
name: "glob-natives-plugin", name: "glob-natives-plugin",
setup: build => { setup: build => {
const filter = /^~pluginNatives$/; const filter = /^~pluginNatives$/;
@ -104,26 +100,26 @@ const globNativesPlugin = {
await Promise.all([ await Promise.all([
// Discord Desktop main & renderer & preload // Discord Desktop main & renderer & preload
esbuild.build({ addBuild({
...nodeCommonOpts, ...nodeCommonOpts,
entryPoints: ["src/main/index.ts"], entryPoints: ["src/main/index.ts"],
outfile: "dist/patcher.js", outfile: "dist/desktop/patcher.js",
footer: { js: "//# sourceURL=VencordPatcher\n" + sourceMapFooter("patcher") }, footer: { js: "//# sourceURL=VencordPatcher\n" + sourceMapFooter("patcher") },
sourcemap, sourcemap,
define: { define: {
...defines, ...defines,
IS_DISCORD_DESKTOP: true, IS_DISCORD_DESKTOP: "true",
IS_VESKTOP: false IS_VESKTOP: "false"
}, },
plugins: [ plugins: [
...nodeCommonOpts.plugins, ...nodeCommonOpts.plugins,
globNativesPlugin globNativesPlugin
] ]
}), }),
esbuild.build({ addBuild({
...commonOpts, ...commonOpts,
entryPoints: ["src/Vencord.ts"], entryPoints: ["src/Vencord.ts"],
outfile: "dist/renderer.js", outfile: "dist/desktop/renderer.js",
format: "iife", format: "iife",
target: ["esnext"], target: ["esnext"],
footer: { js: "//# sourceURL=VencordRenderer\n" + sourceMapFooter("renderer") }, footer: { js: "//# sourceURL=VencordRenderer\n" + sourceMapFooter("renderer") },
@ -135,47 +131,47 @@ await Promise.all([
], ],
define: { define: {
...defines, ...defines,
IS_DISCORD_DESKTOP: true, IS_DISCORD_DESKTOP: "true",
IS_VESKTOP: false IS_VESKTOP: "false"
} }
}), }),
esbuild.build({ addBuild({
...nodeCommonOpts, ...nodeCommonOpts,
entryPoints: ["src/preload.ts"], entryPoints: ["src/preload.ts"],
outfile: "dist/preload.js", outfile: "dist/desktop/preload.js",
footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") }, footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") },
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({ addBuild({
...nodeCommonOpts, ...nodeCommonOpts,
entryPoints: ["src/main/index.ts"], entryPoints: ["src/main/index.ts"],
outfile: "dist/vencordDesktopMain.js", outfile: "dist/vesktop/main.js",
footer: { js: "//# sourceURL=VencordDesktopMain\n" + sourceMapFooter("vencordDesktopMain") }, footer: { js: "//# sourceURL=VencordMain\n" + sourceMapFooter("main") },
sourcemap, sourcemap,
define: { define: {
...defines, ...defines,
IS_DISCORD_DESKTOP: false, IS_DISCORD_DESKTOP: "false",
IS_VESKTOP: true IS_VESKTOP: "true"
}, },
plugins: [ plugins: [
...nodeCommonOpts.plugins, ...nodeCommonOpts.plugins,
globNativesPlugin globNativesPlugin
] ]
}), }),
esbuild.build({ addBuild({
...commonOpts, ...commonOpts,
entryPoints: ["src/Vencord.ts"], entryPoints: ["src/Vencord.ts"],
outfile: "dist/vencordDesktopRenderer.js", outfile: "dist/vesktop/renderer.js",
format: "iife", format: "iife",
target: ["esnext"], target: ["esnext"],
footer: { js: "//# sourceURL=VencordDesktopRenderer\n" + sourceMapFooter("vencordDesktopRenderer") }, footer: { js: "//# sourceURL=VencordRenderer\n" + sourceMapFooter("renderer") },
globalName: "Vencord", globalName: "Vencord",
sourcemap, sourcemap,
plugins: [ plugins: [
@ -184,26 +180,58 @@ await Promise.all([
], ],
define: { define: {
...defines, ...defines,
IS_DISCORD_DESKTOP: false, IS_DISCORD_DESKTOP: "false",
IS_VESKTOP: true IS_VESKTOP: "true"
} }
}), }),
esbuild.build({ addBuild({
...nodeCommonOpts, ...nodeCommonOpts,
entryPoints: ["src/preload.ts"], entryPoints: ["src/preload.ts"],
outfile: "dist/vencordDesktopPreload.js", outfile: "dist/vesktop/preload.js",
footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("vencordDesktopPreload") }, footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") },
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();
// make ci fail
if (!commonOpts.watch) await Promise.all([
process.exitCode = 1; writeFile("dist/desktop/package.json", JSON.stringify({
}); name: "vencord",
main: "patcher.js"
})),
writeFile("dist/vesktop/package.json", JSON.stringify({
name: "vencord",
main: "main.js"
}))
]);
await Promise.all([
createPackage("dist/desktop", "dist/desktop.asar"),
createPackage("dist/vesktop", "dist/vesktop.asar")
]);
if (existsSync("dist/renderer.js")) {
console.warn("Legacy dist folder. Cleaning up and adding shims.");
await Promise.all(
readdirSync("dist")
.filter(f =>
f.endsWith(".map") ||
f.endsWith(".LEGAL.txt") ||
["patcher", "preload", "renderer"].some(name => f.startsWith(name))
)
.map(file => rm(join("dist", file)))
);
await Promise.all([
writeFile("dist/patcher.js", 'require("./desktop")'),
writeFile("dist/vencordDesktopMain.js", 'require("./vesktop")')
]);
}

View file

@ -23,12 +23,12 @@ 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 } from "./common.mjs"; import { addBuild, BUILD_TIMESTAMP, buildOrWatchAll, commonOpts, globPlugins, IS_DEV, IS_REPORTER, VERSION } from "./common.mjs";
/** /**
* @type {esbuild.BuildOptions} * @type {esbuild.BuildOptions}
*/ */
const commonOptions = { const commonOptions: esbuild.BuildOptions = {
...commonOpts, ...commonOpts,
entryPoints: ["browser/Vencord.ts"], entryPoints: ["browser/Vencord.ts"],
globalName: "Vencord", globalName: "Vencord",
@ -40,16 +40,16 @@ const commonOptions = {
], ],
target: ["esnext"], target: ["esnext"],
define: { define: {
IS_WEB: true, IS_WEB: "true",
IS_EXTENSION: false, IS_EXTENSION: "false",
IS_STANDALONE: true, IS_STANDALONE: "true",
IS_DEV, IS_DEV: String(IS_DEV),
IS_REPORTER, IS_REPORTER: String(IS_REPORTER),
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: JSON.stringify(VERSION),
BUILD_TIMESTAMP BUILD_TIMESTAMP: String(BUILD_TIMESTAMP)
} }
}; };
@ -67,39 +67,39 @@ const RnNoiseFiles = [
await Promise.all( await Promise.all(
[ [
esbuild.build({ addBuild({
entryPoints: MonacoWorkerEntryPoints.map(entry => `node_modules/monaco-editor/esm/${entry}`), entryPoints: MonacoWorkerEntryPoints.map(entry => `node_modules/monaco-editor/esm/${entry}`),
bundle: true, bundle: true,
minify: true, minify: true,
format: "iife", format: "iife",
outbase: "node_modules/monaco-editor/esm/", outbase: "node_modules/monaco-editor/esm/",
outdir: "dist/monaco" outdir: "dist/browser/monaco"
}), }),
esbuild.build({ addBuild({
entryPoints: ["browser/monaco.ts"], entryPoints: ["browser/monaco.ts"],
bundle: true, bundle: true,
minify: true, minify: true,
format: "iife", format: "iife",
outfile: "dist/monaco/index.js", outfile: "dist/browser/monaco/index.js",
loader: { loader: {
".ttf": "file" ".ttf": "file"
} }
}), }),
esbuild.build({ addBuild({
...commonOptions, ...commonOptions,
outfile: "dist/browser.js", outfile: "dist/browser/browser.js",
footer: { js: "//# sourceURL=VencordWeb" } footer: { js: "//# sourceURL=VencordWeb" }
}), }),
esbuild.build({ addBuild({
...commonOptions, ...commonOptions,
outfile: "dist/extension.js", outfile: "dist/browser/extension.js",
define: { define: {
...commonOptions?.define, ...commonOptions?.define,
IS_EXTENSION: true, IS_EXTENSION: "true",
}, },
footer: { js: "//# sourceURL=VencordWeb" } footer: { js: "//# sourceURL=VencordWeb" }
}), }),
esbuild.build({ addBuild({
...commonOptions, ...commonOptions,
inject: ["browser/GMPolyfill.js", ...(commonOptions?.inject || [])], inject: ["browser/GMPolyfill.js", ...(commonOptions?.inject || [])],
define: { define: {
@ -118,11 +118,10 @@ await Promise.all(
] ]
); );
/** await buildOrWatchAll();
* @type {(dir: string) => Promise<string[]>}
*/ async function globDir(dir: string): Promise<string[]> {
async function globDir(dir) { const files = [] as string[];
const files = [];
for (const child of await readdir(dir, { withFileTypes: true })) { for (const child of await readdir(dir, { withFileTypes: true })) {
const p = join(dir, child.name); const p = join(dir, child.name);
@ -135,27 +134,23 @@ async function globDir(dir) {
return files; return files;
} }
/** async function loadDir(dir: string, basePath = "") {
* @type {(dir: string, basePath?: string) => Promise<Record<string, string>>}
*/
async function loadDir(dir, basePath = "") {
const files = await globDir(dir); const files = await globDir(dir);
return Object.fromEntries(await Promise.all(files.map(async f => [f.slice(basePath.length), await readFile(f)]))); return Object.fromEntries(await Promise.all(files.map(async f =>
[f.slice(basePath.length), await readFile(f)] as const
)));
} }
/** async function buildExtension(target: string, files: string[]): Promise<void> {
* @type {(target: string, files: string[]) => Promise<void>} const entries: Record<string, Buffer> = {
*/ "dist/Vencord.js": await readFile("dist/browser/extension.js"),
async function buildExtension(target, files) { "dist/Vencord.css": await readFile("dist/browser/extension.css"),
const entries = { ...await loadDir("dist/browser/monaco"),
"dist/Vencord.js": await readFile("dist/extension.js"),
"dist/Vencord.css": await readFile("dist/extension.css"),
...await loadDir("dist/monaco"),
...Object.fromEntries(await Promise.all(RnNoiseFiles.map(async file => ...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}`)] [`third-party/rnnoise/${file.replace(/^dist\//, "")}`, await readFile(`node_modules/@sapphi-red/web-noise-suppressor/${file}`)] as const
))), ))),
...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: Uint8Array | Buffer = 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;
@ -165,19 +160,19 @@ async function buildExtension(target, files) {
return [ return [
f.startsWith("manifest") ? "manifest.json" : f, f.startsWith("manifest") ? "manifest.json" : f,
content content
]; ] as const;
}))) })))
}; };
await rm(target, { recursive: true, force: true }); await rm(target, { recursive: true, force: true });
await Promise.all(Object.entries(entries).map(async ([file, content]) => { await Promise.all(Object.entries(entries).map(async ([file, content]) => {
const dest = join("dist", target, file); const dest = join("dist/browser", target, file);
const parentDirectory = join(dest, ".."); const parentDirectory = join(dest, "..");
await mkdir(parentDirectory, { recursive: true }); await mkdir(parentDirectory, { recursive: true });
await writeFile(dest, content); await writeFile(dest, content);
})); }));
console.info("Unpacked Extension written to dist/" + target); console.info("Unpacked Extension written to dist/browser/" + target);
} }
const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content => { const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content => {
@ -200,12 +195,14 @@ if (!process.argv.includes("--skip-extension")) {
buildExtension("firefox-unpacked", ["background.js", "content.js", "manifestv2.json", "icon.png"]), buildExtension("firefox-unpacked", ["background.js", "content.js", "manifestv2.json", "icon.png"]),
]); ]);
Zip.sync.zip("dist/chromium-unpacked").compress().save("dist/extension-chrome.zip"); Zip.zip("dist/browser/chromium-unpacked", (_err, zip) => {
zip.compress().save("dist/extension-chrome.zip");
console.info("Packed Chromium Extension written to dist/extension-chrome.zip"); console.info("Packed Chromium Extension written to dist/extension-chrome.zip");
});
Zip.sync.zip("dist/firefox-unpacked").compress().save("dist/extension-firefox.zip"); Zip.zip("dist/browser/firefox-unpacked", (_err, zip) => {
zip.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;
} }

View file

@ -20,7 +20,7 @@ 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, BuildOptions, context, Plugin } 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";
@ -29,8 +29,7 @@ import { promisify } from "util";
import { getPluginTarget } from "../utils.mjs"; import { getPluginTarget } from "../utils.mjs";
/** @type {import("../../package.json")} */ const PackageJSON: typeof import("../../package.json") = JSON.parse(readFileSync("package.json", "utf-8"));
const PackageJSON = JSON.parse(readFileSync("package.json"));
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,11 +53,8 @@ export const banner = {
}; };
const PluginDefinitionNameMatcher = /definePlugin\(\{\s*(["'])?name\1:\s*(["'`])(.+?)\2/; const PluginDefinitionNameMatcher = /definePlugin\(\{\s*(["'])?name\1:\s*(["'`])(.+?)\2/;
/**
* @param {string} base export async function resolvePluginName(base: string, dirent: import("fs").Dirent) {
* @param {import("fs").Dirent} dirent
*/
export async function resolvePluginName(base, dirent) {
const fullPath = join(base, dirent.name); const fullPath = join(base, dirent.name);
const content = dirent.isFile() const content = dirent.isFile()
? await readFile(fullPath, "utf-8") ? await readFile(fullPath, "utf-8")
@ -79,28 +75,13 @@ export async function resolvePluginName(base, dirent) {
})(); })();
} }
export async function exists(path) { export async function exists(path: string) {
return await access(path, FsConstants.F_OK) return await access(path, FsConstants.F_OK)
.then(() => true) .then(() => true)
.catch(() => false); .catch(() => false);
} }
// https://github.com/evanw/esbuild/issues/619#issuecomment-751995294 export const globPlugins: (kind: "web" | "discordDesktop" | "vencordDesktop") => Plugin = kind => ({
/**
* @type {import("esbuild").Plugin}
*/
export const makeAllPackagesExternalPlugin = {
name: "make-all-packages-external",
setup(build) {
const filter = /^[^./]|^\.[^./]|^\.\.[^/]/; // Must not start with "/" or "./" or "../"
build.onResolve({ filter }, args => ({ path: args.path, external: true }));
}
};
/**
* @type {(kind: "web" | "discordDesktop" | "vencordDesktop") => import("esbuild").Plugin}
*/
export const globPlugins = kind => ({
name: "glob-plugins", name: "glob-plugins",
setup: build => { setup: build => {
const filter = /^~plugins$/; const filter = /^~plugins$/;
@ -164,10 +145,7 @@ export const globPlugins = kind => ({
} }
}); });
/** export const gitHashPlugin: Plugin = {
* @type {import("esbuild").Plugin}
*/
export const gitHashPlugin = {
name: "git-hash-plugin", name: "git-hash-plugin",
setup: build => { setup: build => {
const filter = /^~git-hash$/; const filter = /^~git-hash$/;
@ -180,10 +158,7 @@ export const gitHashPlugin = {
} }
}; };
/** export const gitRemotePlugin: Plugin = {
* @type {import("esbuild").Plugin}
*/
export const gitRemotePlugin = {
name: "git-remote-plugin", name: "git-remote-plugin",
setup: build => { setup: build => {
const filter = /^~git-remote$/; const filter = /^~git-remote$/;
@ -205,10 +180,7 @@ export const gitRemotePlugin = {
} }
}; };
/** export const fileUrlPlugin: Plugin = {
* @type {import("esbuild").Plugin}
*/
export const fileUrlPlugin = {
name: "file-uri-plugin", name: "file-uri-plugin",
setup: build => { setup: build => {
const filter = /^file:\/\/.+$/; const filter = /^file:\/\/.+$/;
@ -228,7 +200,7 @@ export const fileUrlPlugin = {
const encoding = base64 ? "base64" : "utf-8"; const encoding = base64 ? "base64" : "utf-8";
let content; let content: string;
if (!minify) { if (!minify) {
content = await readFile(path, encoding); content = await readFile(path, encoding);
if (!noTrim) content = content.trimEnd(); if (!noTrim) content = content.trimEnd();
@ -268,10 +240,7 @@ export const fileUrlPlugin = {
}; };
const styleModule = readFileSync("./scripts/build/module/style.js", "utf-8"); const styleModule = readFileSync("./scripts/build/module/style.js", "utf-8");
/** export const stylePlugin: Plugin = {
* @type {import("esbuild").Plugin}
*/
export const stylePlugin = {
name: "style-plugin", name: "style-plugin",
setup: ({ onResolve, onLoad }) => { setup: ({ onResolve, onLoad }) => {
onResolve({ filter: /\.css\?managed$/, namespace: "file" }, ({ path, resolveDir }) => ({ onResolve({ filter: /\.css\?managed$/, namespace: "file" }, ({ path, resolveDir }) => ({
@ -292,22 +261,59 @@ export const stylePlugin = {
} }
}; };
/** let buildsFinished = Promise.resolve();
* @type {import("esbuild").BuildOptions} const buildsFinishedPlugin: Plugin = {
*/ name: "builds-finished-plugin",
setup({ onEnd }) {
if (!watch) return;
let resolve: () => void;
const done = new Promise<void>(r => resolve = r);
buildsFinished = buildsFinished.then(() => done);
onEnd(() => resolve());
},
};
export const commonOpts = { export const commonOpts = {
logLevel: "info", logLevel: "info",
bundle: true, bundle: true,
watch,
minify: !watch, minify: !watch,
sourcemap: watch ? "inline" : "", sourcemap: watch ? "inline" : "external",
legalComments: "linked", legalComments: "linked",
banner, banner,
plugins: [fileUrlPlugin, gitHashPlugin, gitRemotePlugin, stylePlugin], plugins: [fileUrlPlugin, gitHashPlugin, gitRemotePlugin, stylePlugin, buildsFinishedPlugin],
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"],
jsxFactory: "VencordCreateElement", jsxFactory: "VencordCreateElement",
jsxFragment: "VencordFragment", jsxFragment: "VencordFragment",
// Work around https://github.com/evanw/esbuild/issues/2460 jsx: "transform"
tsconfig: "./scripts/build/tsconfig.esbuild.json" } satisfies BuildOptions;
};
const builds = [] as BuildOptions[];
export function addBuild(options: BuildOptions) {
builds.push(options);
}
export async function buildOrWatchAll() {
if (watch) {
const contexts = await Promise.all(builds.map(context));
await Promise.all(contexts.map(ctx => ctx.watch()));
await buildsFinished;
} else {
try {
await Promise.all(builds.map(build));
} catch (err) {
const reason = err instanceof Error
? err.message
: err;
console.error("Build failed");
console.error(reason);
// make ci fail
process.exitCode = 1;
}
}
}

View file

@ -1,7 +0,0 @@
// Work around https://github.com/evanw/esbuild/issues/2460
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"jsx": "react"
}
}

View file

@ -124,6 +124,7 @@ try {
env: { env: {
...process.env, ...process.env,
VENCORD_USER_DATA_DIR: BASE_DIR, VENCORD_USER_DATA_DIR: BASE_DIR,
VENCORD_DIRECTORY: join(BASE_DIR, "dist/desktop"),
VENCORD_DEV_INSTALL: "1" VENCORD_DEV_INSTALL: "1"
} }
}); });

View file

@ -16,15 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
/** export function getPluginTarget(filePath: string) {
* @param {string} filePath
* @returns {string | null}
*/
export function getPluginTarget(filePath) {
const pathParts = filePath.split(/[/\\]/); const pathParts = filePath.split(/[/\\]/);
if (/^index\.tsx?$/.test(pathParts.at(-1))) pathParts.pop(); if (/^index\.tsx?$/.test(pathParts.at(-1)!)) pathParts.pop();
const identifier = pathParts.at(-1).replace(/\.tsx?$/, ""); const identifier = pathParts.at(-1)!.replace(/\.tsx?$/, "");
const identiferBits = identifier.split("."); const identiferBits = identifier.split(".");
return identiferBits.length === 1 ? null : identiferBits.at(-1); return identiferBits.length === 1 ? null : identiferBits.at(-1);
} }

View file

@ -43,11 +43,9 @@ if (IS_VESKTOP || !IS_VANILLA) {
} }
switch (url) { switch (url) {
case "renderer.js.map": case "renderer.js.map":
case "vencordDesktopRenderer.js.map":
case "preload.js.map": case "preload.js.map":
case "vencordDesktopPreload.js.map":
case "patcher.js.map": case "patcher.js.map":
case "vencordDesktopMain.js.map": case "main.js.map":
cb(join(__dirname, url)); cb(join(__dirname, url));
break; break;
default: default:

View file

@ -131,7 +131,7 @@ ipcMain.handle(IpcEvents.OPEN_MONACO_EDITOR, async () => {
autoHideMenuBar: true, autoHideMenuBar: true,
darkTheme: true, darkTheme: true,
webPreferences: { webPreferences: {
preload: join(__dirname, IS_DISCORD_DESKTOP ? "preload.js" : "vencordDesktopPreload.js"), preload: join(__dirname, "preload.js"),
contextIsolation: true, contextIsolation: true,
nodeIntegration: false, nodeIntegration: false,
sandbox: false sandbox: false

View file

@ -26,14 +26,14 @@ import { IS_VANILLA } from "./utils/constants";
console.log("[Vencord] Starting up..."); console.log("[Vencord] Starting up...");
// FIXME: remove at some point
export const isLegacyNonAsarVencord = IS_STANDALONE && !__dirname.endsWith(".asar");
// Our injector file at app/index.js // Our injector file at app/index.js
const injectorPath = require.main!.filename; const injectorPath = require.main!.filename;
// special discord_arch_electron injection method
const asarName = require.main!.path.endsWith("app.asar") ? "_app.asar" : "app.asar";
// The original app.asar // The original app.asar
const asarPath = join(dirname(injectorPath), "..", asarName); const asarPath = join(dirname(injectorPath), "..", "_app.asar");
const discordPkg = require(join(asarPath, "package.json")); const discordPkg = require(join(asarPath, "package.json"));
require.main!.filename = join(asarPath, discordPkg.main); require.main!.filename = join(asarPath, discordPkg.main);
@ -41,7 +41,7 @@ require.main!.filename = join(asarPath, discordPkg.main);
// @ts-ignore Untyped method? Dies from cringe // @ts-ignore Untyped method? Dies from cringe
app.setAppPath(asarPath); app.setAppPath(asarPath);
if (!IS_VANILLA) { if (!IS_VANILLA && !isLegacyNonAsarVencord) {
const settings = RendererSettings.store; const settings = RendererSettings.store;
// Repatch after host updates on Windows // Repatch after host updates on Windows
if (process.platform === "win32") { if (process.platform === "win32") {
@ -71,7 +71,7 @@ if (!IS_VANILLA) {
constructor(options: BrowserWindowConstructorOptions) { constructor(options: BrowserWindowConstructorOptions) {
if (options?.webPreferences?.preload && options.title) { if (options?.webPreferences?.preload && options.title) {
const original = options.webPreferences.preload; const original = options.webPreferences.preload;
options.webPreferences.preload = join(__dirname, IS_DISCORD_DESKTOP ? "preload.js" : "vencordDesktopPreload.js"); options.webPreferences.preload = join(__dirname, "preload.js");
options.webPreferences.sandbox = false; options.webPreferences.sandbox = false;
// work around discord unloading when in background // work around discord unloading when in background
options.webPreferences.backgroundThrottling = false; options.webPreferences.backgroundThrottling = false;
@ -157,5 +157,7 @@ if (!IS_VANILLA) {
console.log("[Vencord] Running in vanilla mode. Not loading Vencord"); console.log("[Vencord] Running in vanilla mode. Not loading Vencord");
} }
console.log("[Vencord] Loading original Discord app.asar"); if (!isLegacyNonAsarVencord) {
require(require.main!.filename); console.log("[Vencord] Loading original Discord app.asar");
require(require.main!.filename);
}

View file

@ -16,12 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
export const VENCORD_FILES = [ export const ASAR_FILE = IS_VESKTOP
IS_DISCORD_DESKTOP ? "patcher.js" : "vencordDesktopMain.js", ? "vesktop.asar"
IS_DISCORD_DESKTOP ? "preload.js" : "vencordDesktopPreload.js", : "desktop.asar";
IS_DISCORD_DESKTOP ? "renderer.js" : "vencordDesktopRenderer.js",
IS_DISCORD_DESKTOP ? "renderer.css" : "vencordDesktopRenderer.css",
];
export function serializeErrors(func: (...args: any[]) => any) { export function serializeErrors(func: (...args: any[]) => any) {
return async function () { return async function () {

View file

@ -16,20 +16,27 @@
* 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 { isLegacyNonAsarVencord } from "@main/patcher";
import { IpcEvents } from "@shared/IpcEvents"; import { IpcEvents } from "@shared/IpcEvents";
import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent"; import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent";
import { ipcMain } from "electron"; import { app, dialog, ipcMain } from "electron";
import { writeFile } from "fs/promises"; import {
existsSync as originalExistsSync,
renameSync as originalRenameSync,
writeFileSync as originalWriteFileSync,
} from "original-fs";
import { join } from "path"; import { join } from "path";
import gitHash from "~git-hash"; import gitHash from "~git-hash";
import gitRemote from "~git-remote"; import gitRemote from "~git-remote";
import { get } from "../utils/simpleGet"; import { get } from "../utils/simpleGet";
import { serializeErrors, VENCORD_FILES } from "./common"; import { ASAR_FILE, serializeErrors } from "./common";
const API_BASE = `https://api.github.com/repos/${gitRemote}`; const API_BASE = `https://api.github.com/repos/${gitRemote}`;
let PendingUpdates = [] as [string, string][]; let PendingUpdate: string | null = null;
let hasUpdateToApplyOnQuit = false;
async function githubGet(endpoint: string) { async function githubGet(endpoint: string) {
return get(API_BASE + endpoint, { return get(API_BASE + endpoint, {
@ -65,22 +72,22 @@ async function fetchUpdates() {
if (hash === gitHash) if (hash === gitHash)
return false; return false;
data.assets.forEach(({ name, browser_download_url }) => {
if (VENCORD_FILES.some(s => name.startsWith(s))) { const asset = data.assets.find(a => a.name === ASAR_FILE);
PendingUpdates.push([name, browser_download_url]); PendingUpdate = asset.browser_download_url;
}
});
return true; return true;
} }
async function applyUpdates() { async function applyUpdates() {
await Promise.all(PendingUpdates.map( if (!PendingUpdate) return true;
async ([name, data]) => writeFile(
join(__dirname, name), const data = await get(PendingUpdate);
await get(data) originalWriteFileSync(__dirname + ".new", data);
) hasUpdateToApplyOnQuit = true;
));
PendingUpdates = []; PendingUpdate = null;
return true; return true;
} }
@ -88,3 +95,51 @@ ipcMain.handle(IpcEvents.GET_REPO, serializeErrors(() => `https://github.com/${g
ipcMain.handle(IpcEvents.GET_UPDATES, serializeErrors(calculateGitChanges)); ipcMain.handle(IpcEvents.GET_UPDATES, serializeErrors(calculateGitChanges));
ipcMain.handle(IpcEvents.UPDATE, serializeErrors(fetchUpdates)); ipcMain.handle(IpcEvents.UPDATE, serializeErrors(fetchUpdates));
ipcMain.handle(IpcEvents.BUILD, serializeErrors(applyUpdates)); ipcMain.handle(IpcEvents.BUILD, serializeErrors(applyUpdates));
async function migrateLegacyToAsar() {
try {
const isFlatpak = process.platform === "linux" && !!process.env.FLATPAK_ID;
if (isFlatpak) throw "Flatpak Discord can't automatically be migrated.";
const data = await get(`https://github.com/${gitRemote}/releases/latest/download/desktop.asar`);
originalWriteFileSync(join(__dirname, "../vencord.asar"), data);
originalWriteFileSync(__filename, '// Legacy shim for new asar\n\nrequire("../vencord.asar");');
app.relaunch();
app.exit();
} catch (e) {
console.error("Failed to migrate to asar", e);
app.whenReady().then(() => {
dialog.showErrorBox(
"Legacy Install",
"The way Vencord loaded was changed and the updater failed to migrate. Please reinstall using the Vencord Installer!"
);
app.exit(1);
});
}
}
function applyPreviousUpdate() {
originalRenameSync(__dirname + ".new", __dirname);
app.relaunch();
app.exit();
}
app.on("will-quit", () => {
if (hasUpdateToApplyOnQuit)
originalRenameSync(__dirname + ".new", __dirname);
});
if (isLegacyNonAsarVencord) {
console.warn("This is a legacy non asar install! Migrating to asar and restarting...");
migrateLegacyToAsar();
}
if (originalExistsSync(__dirname + ".new")) {
console.warn("Found previous not applied update, applying now and restarting...");
applyPreviousUpdate();
}

View file

@ -117,7 +117,7 @@ export default definePlugin({
wrapSort(comparator: Function, row: any) { wrapSort(comparator: Function, row: any) {
return row.type === 5 return row.type === 5
? -UserAffinitiesStore.getUserAffinity(row.user.id)?.affinity ?? 0 ? -(UserAffinitiesStore.getUserAffinity(row.user.id)?.affinity ?? 0)
: comparator(row); : comparator(row);
}, },

View file

@ -28,7 +28,7 @@ contextBridge.exposeInMainWorld("VencordNative", VencordNative);
// Discord // Discord
if (location.protocol !== "data:") { if (location.protocol !== "data:") {
// #region cssInsert // #region cssInsert
const rendererCss = join(__dirname, IS_VESKTOP ? "vencordDesktopRenderer.css" : "renderer.css"); const rendererCss = join(__dirname, "renderer.css");
const style = document.createElement("style"); const style = document.createElement("style");
style.id = "vencord-css-core"; style.id = "vencord-css-core";

View file

@ -299,7 +299,7 @@ export const lazyWebpackSearchHistory = [] as Array<["find" | "findByProps" | "f
* Note that the example below exists already as an api, see {@link findByPropsLazy} * Note that the example below exists already as an api, see {@link findByPropsLazy}
* @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah); * @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah);
*/ */
export function proxyLazyWebpack<T = any>(factory: () => any, attempts?: number) { export function proxyLazyWebpack<T = any>(factory: () => T, attempts?: number) {
if (IS_REPORTER) lazyWebpackSearchHistory.push(["proxyLazyWebpack", [factory]]); if (IS_REPORTER) lazyWebpackSearchHistory.push(["proxyLazyWebpack", [factory]]);
return proxyLazy<T>(factory, attempts); return proxyLazy<T>(factory, attempts);