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
run: pnpm generatePluginJson dist/plugins.json dist/plugin-readmes.json
- name: Clean up obsolete files
- name: Collect files to be released
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
id: release_values
@ -50,16 +69,14 @@ jobs:
echo "release_tag=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
- name: Upload DevBuild as release
if: github.repository == 'Vendicated/Vencord'
run: |
gh release upload devbuild --clobber dist/*
gh release upload devbuild --clobber dist/release/*
gh release edit devbuild --title "DevBuild $RELEASE_TAG"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ env.release_tag }}
- name: Upload DevBuild to builds repo
if: github.repository == 'Vendicated/Vencord'
run: |
git config --global user.name "$USERNAME"
git config --global user.email actions@github.com
@ -69,7 +86,7 @@ jobs:
GLOBIGNORE=.git:.gitignore:README.md:LICENSE
rm -rf *
cp -r ../dist/* .
cp -r ../dist/release/* .
git add -A
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
- name: Use Node.js 19
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
@ -36,7 +36,7 @@ jobs:
- name: Publish extension
run: |
cd dist/chromium-unpacked
cd dist/browser/chromium-unpacked
pnpx chrome-webstore-upload-cli@2.1.0 upload --auto-publish
env:
EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}

View file

@ -14,9 +14,9 @@
"license": "GPL-3.0-or-later",
"author": "Vendicated",
"scripts": {
"build": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/build.mjs",
"build": "tsx scripts/build/build.mts",
"buildStandalone": "pnpm build --standalone",
"buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs",
"buildWeb": "tsx scripts/build/buildWeb.mts",
"buildWebStandalone": "pnpm buildWeb --standalone",
"buildReporter": "pnpm buildWebStandalone --reporter --skip-extension",
"buildReporterDesktop": "pnpm build --reporter",
@ -46,6 +46,7 @@
"virtual-merge": "^1.0.1"
},
"devDependencies": {
"@electron/asar": "^3.2.10",
"@types/chrome": "^0.0.246",
"@types/diff": "^5.0.3",
"@types/lodash": "^4.14.194",
@ -57,7 +58,7 @@
"@typescript-eslint/parser": "^5.59.1",
"diff": "^5.1.0",
"discord-types": "^1.3.26",
"esbuild": "^0.15.18",
"esbuild": "^0.23.0",
"eslint": "^8.46.0",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-path-alias": "^1.0.0",
@ -71,7 +72,7 @@
"stylelint": "^15.6.0",
"stylelint-config-standard": "^33.0.0",
"ts-patch": "^3.1.2",
"tsx": "^3.12.7",
"tsx": "^4.16.2",
"type-fest": "^3.9.0",
"typescript": "^5.4.5",
"typescript-transform-paths": "^3.4.7",
@ -104,7 +105,7 @@
"sourceDir": "./dist/firefox-unpacked"
},
"engines": {
"node": ">=18",
"node": ">=20",
"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/>.
*/
import esbuild from "esbuild";
import { readdir } from "fs/promises";
import { createPackage } from "@electron/asar";
import { BuildOptions, Plugin } from "esbuild";
import { existsSync, readdirSync } from "fs";
import { readdir, rm, writeFile } from "fs/promises";
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 = {
IS_STANDALONE,
IS_DEV,
IS_REPORTER,
IS_UPDATER_DISABLED,
IS_WEB: false,
IS_EXTENSION: false,
IS_STANDALONE: String(IS_STANDALONE),
IS_DEV: String(IS_DEV),
IS_REPORTER: String(IS_REPORTER),
IS_UPDATER_DISABLED: String(IS_UPDATER_DISABLED),
IS_WEB: "false",
IS_EXTENSION: "false",
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
// for the specific platform we're on
defines["process.platform"] = JSON.stringify(process.platform);
/**
* @type {esbuild.BuildOptions}
*/
const nodeCommonOpts = {
...commonOpts,
format: "cjs",
@ -49,15 +48,12 @@ const nodeCommonOpts = {
target: ["esnext"],
external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external],
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";
/**
* @type {import("esbuild").Plugin}
*/
const globNativesPlugin = {
const globNativesPlugin: Plugin = {
name: "glob-natives-plugin",
setup: build => {
const filter = /^~pluginNatives$/;
@ -104,26 +100,26 @@ const globNativesPlugin = {
await Promise.all([
// Discord Desktop main & renderer & preload
esbuild.build({
addBuild({
...nodeCommonOpts,
entryPoints: ["src/main/index.ts"],
outfile: "dist/patcher.js",
outfile: "dist/desktop/patcher.js",
footer: { js: "//# sourceURL=VencordPatcher\n" + sourceMapFooter("patcher") },
sourcemap,
define: {
...defines,
IS_DISCORD_DESKTOP: true,
IS_VESKTOP: false
IS_DISCORD_DESKTOP: "true",
IS_VESKTOP: "false"
},
plugins: [
...nodeCommonOpts.plugins,
globNativesPlugin
]
}),
esbuild.build({
addBuild({
...commonOpts,
entryPoints: ["src/Vencord.ts"],
outfile: "dist/renderer.js",
outfile: "dist/desktop/renderer.js",
format: "iife",
target: ["esnext"],
footer: { js: "//# sourceURL=VencordRenderer\n" + sourceMapFooter("renderer") },
@ -135,47 +131,47 @@ await Promise.all([
],
define: {
...defines,
IS_DISCORD_DESKTOP: true,
IS_VESKTOP: false
IS_DISCORD_DESKTOP: "true",
IS_VESKTOP: "false"
}
}),
esbuild.build({
addBuild({
...nodeCommonOpts,
entryPoints: ["src/preload.ts"],
outfile: "dist/preload.js",
outfile: "dist/desktop/preload.js",
footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") },
sourcemap,
define: {
...defines,
IS_DISCORD_DESKTOP: true,
IS_VESKTOP: false
IS_DISCORD_DESKTOP: "true",
IS_VESKTOP: "false"
}
}),
// Vencord Desktop main & renderer & preload
esbuild.build({
addBuild({
...nodeCommonOpts,
entryPoints: ["src/main/index.ts"],
outfile: "dist/vencordDesktopMain.js",
footer: { js: "//# sourceURL=VencordDesktopMain\n" + sourceMapFooter("vencordDesktopMain") },
outfile: "dist/vesktop/main.js",
footer: { js: "//# sourceURL=VencordMain\n" + sourceMapFooter("main") },
sourcemap,
define: {
...defines,
IS_DISCORD_DESKTOP: false,
IS_VESKTOP: true
IS_DISCORD_DESKTOP: "false",
IS_VESKTOP: "true"
},
plugins: [
...nodeCommonOpts.plugins,
globNativesPlugin
]
}),
esbuild.build({
addBuild({
...commonOpts,
entryPoints: ["src/Vencord.ts"],
outfile: "dist/vencordDesktopRenderer.js",
outfile: "dist/vesktop/renderer.js",
format: "iife",
target: ["esnext"],
footer: { js: "//# sourceURL=VencordDesktopRenderer\n" + sourceMapFooter("vencordDesktopRenderer") },
footer: { js: "//# sourceURL=VencordRenderer\n" + sourceMapFooter("renderer") },
globalName: "Vencord",
sourcemap,
plugins: [
@ -184,26 +180,58 @@ await Promise.all([
],
define: {
...defines,
IS_DISCORD_DESKTOP: false,
IS_VESKTOP: true
IS_DISCORD_DESKTOP: "false",
IS_VESKTOP: "true"
}
}),
esbuild.build({
addBuild({
...nodeCommonOpts,
entryPoints: ["src/preload.ts"],
outfile: "dist/vencordDesktopPreload.js",
footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("vencordDesktopPreload") },
outfile: "dist/vesktop/preload.js",
footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") },
sourcemap,
define: {
...defines,
IS_DISCORD_DESKTOP: false,
IS_VESKTOP: true
IS_DISCORD_DESKTOP: "false",
IS_VESKTOP: "true"
}
}),
]).catch(err => {
console.error("Build failed");
console.error(err.message);
// make ci fail
if (!commonOpts.watch)
process.exitCode = 1;
});
]);
await buildOrWatchAll();
await Promise.all([
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 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}
*/
const commonOptions = {
const commonOptions: esbuild.BuildOptions = {
...commonOpts,
entryPoints: ["browser/Vencord.ts"],
globalName: "Vencord",
@ -40,16 +40,16 @@ const commonOptions = {
],
target: ["esnext"],
define: {
IS_WEB: true,
IS_EXTENSION: false,
IS_STANDALONE: true,
IS_DEV,
IS_REPORTER,
IS_DISCORD_DESKTOP: false,
IS_VESKTOP: false,
IS_UPDATER_DISABLED: true,
IS_WEB: "true",
IS_EXTENSION: "false",
IS_STANDALONE: "true",
IS_DEV: String(IS_DEV),
IS_REPORTER: String(IS_REPORTER),
IS_DISCORD_DESKTOP: "false",
IS_VESKTOP: "false",
IS_UPDATER_DISABLED: "true",
VERSION: JSON.stringify(VERSION),
BUILD_TIMESTAMP
BUILD_TIMESTAMP: String(BUILD_TIMESTAMP)
}
};
@ -67,39 +67,39 @@ const RnNoiseFiles = [
await Promise.all(
[
esbuild.build({
addBuild({
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"
outdir: "dist/browser/monaco"
}),
esbuild.build({
addBuild({
entryPoints: ["browser/monaco.ts"],
bundle: true,
minify: true,
format: "iife",
outfile: "dist/monaco/index.js",
outfile: "dist/browser/monaco/index.js",
loader: {
".ttf": "file"
}
}),
esbuild.build({
addBuild({
...commonOptions,
outfile: "dist/browser.js",
outfile: "dist/browser/browser.js",
footer: { js: "//# sourceURL=VencordWeb" }
}),
esbuild.build({
addBuild({
...commonOptions,
outfile: "dist/extension.js",
outfile: "dist/browser/extension.js",
define: {
...commonOptions?.define,
IS_EXTENSION: true,
IS_EXTENSION: "true",
},
footer: { js: "//# sourceURL=VencordWeb" }
}),
esbuild.build({
addBuild({
...commonOptions,
inject: ["browser/GMPolyfill.js", ...(commonOptions?.inject || [])],
define: {
@ -118,11 +118,10 @@ await Promise.all(
]
);
/**
* @type {(dir: string) => Promise<string[]>}
*/
async function globDir(dir) {
const files = [];
await buildOrWatchAll();
async function globDir(dir: string): Promise<string[]> {
const files = [] as string[];
for (const child of await readdir(dir, { withFileTypes: true })) {
const p = join(dir, child.name);
@ -135,27 +134,23 @@ async function globDir(dir) {
return files;
}
/**
* @type {(dir: string, basePath?: string) => Promise<Record<string, string>>}
*/
async function loadDir(dir, basePath = "") {
async function loadDir(dir: string, basePath = "") {
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
)));
}
/**
* @type {(target: string, files: string[]) => Promise<void>}
*/
async function buildExtension(target, files) {
const entries = {
"dist/Vencord.js": await readFile("dist/extension.js"),
"dist/Vencord.css": await readFile("dist/extension.css"),
...await loadDir("dist/monaco"),
async function buildExtension(target: string, files: string[]): Promise<void> {
const entries: Record<string, Buffer> = {
"dist/Vencord.js": await readFile("dist/browser/extension.js"),
"dist/Vencord.css": await readFile("dist/browser/extension.css"),
...await loadDir("dist/browser/monaco"),
...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 => {
let content = await readFile(join("browser", f));
let content: Uint8Array | Buffer = await readFile(join("browser", f));
if (f.startsWith("manifest")) {
const json = JSON.parse(content.toString("utf-8"));
json.version = VERSION;
@ -165,19 +160,19 @@ async function buildExtension(target, files) {
return [
f.startsWith("manifest") ? "manifest.json" : f,
content
];
] as const;
})))
};
await rm(target, { recursive: true, force: true });
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, "..");
await mkdir(parentDirectory, { recursive: true });
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 => {
@ -200,12 +195,14 @@ if (!process.argv.includes("--skip-extension")) {
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");
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");
});
} else {
await appendCssRuntime;
}

View file

@ -20,7 +20,7 @@ import "../suppressExperimentalWarnings.js";
import "../checkNodeVersion.js";
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 { access, readdir, readFile } from "fs/promises";
import { minify as minifyHtml } from "html-minifier-terser";
@ -29,8 +29,7 @@ import { promisify } from "util";
import { getPluginTarget } from "../utils.mjs";
/** @type {import("../../package.json")} */
const PackageJSON = JSON.parse(readFileSync("package.json"));
const PackageJSON: typeof import("../../package.json") = JSON.parse(readFileSync("package.json", "utf-8"));
export const VERSION = PackageJSON.version;
// https://reproducible-builds.org/docs/source-date-epoch/
@ -54,11 +53,8 @@ export const banner = {
};
const PluginDefinitionNameMatcher = /definePlugin\(\{\s*(["'])?name\1:\s*(["'`])(.+?)\2/;
/**
* @param {string} base
* @param {import("fs").Dirent} dirent
*/
export async function resolvePluginName(base, dirent) {
export async function resolvePluginName(base: string, dirent: import("fs").Dirent) {
const fullPath = join(base, dirent.name);
const content = dirent.isFile()
? 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)
.then(() => true)
.catch(() => false);
}
// https://github.com/evanw/esbuild/issues/619#issuecomment-751995294
/**
* @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 => ({
export const globPlugins: (kind: "web" | "discordDesktop" | "vencordDesktop") => Plugin = kind => ({
name: "glob-plugins",
setup: build => {
const filter = /^~plugins$/;
@ -164,10 +145,7 @@ export const globPlugins = kind => ({
}
});
/**
* @type {import("esbuild").Plugin}
*/
export const gitHashPlugin = {
export const gitHashPlugin: Plugin = {
name: "git-hash-plugin",
setup: build => {
const filter = /^~git-hash$/;
@ -180,10 +158,7 @@ export const gitHashPlugin = {
}
};
/**
* @type {import("esbuild").Plugin}
*/
export const gitRemotePlugin = {
export const gitRemotePlugin: Plugin = {
name: "git-remote-plugin",
setup: build => {
const filter = /^~git-remote$/;
@ -205,10 +180,7 @@ export const gitRemotePlugin = {
}
};
/**
* @type {import("esbuild").Plugin}
*/
export const fileUrlPlugin = {
export const fileUrlPlugin: Plugin = {
name: "file-uri-plugin",
setup: build => {
const filter = /^file:\/\/.+$/;
@ -228,7 +200,7 @@ export const fileUrlPlugin = {
const encoding = base64 ? "base64" : "utf-8";
let content;
let content: string;
if (!minify) {
content = await readFile(path, encoding);
if (!noTrim) content = content.trimEnd();
@ -268,10 +240,7 @@ export const fileUrlPlugin = {
};
const styleModule = readFileSync("./scripts/build/module/style.js", "utf-8");
/**
* @type {import("esbuild").Plugin}
*/
export const stylePlugin = {
export const stylePlugin: Plugin = {
name: "style-plugin",
setup: ({ onResolve, onLoad }) => {
onResolve({ filter: /\.css\?managed$/, namespace: "file" }, ({ path, resolveDir }) => ({
@ -292,22 +261,59 @@ export const stylePlugin = {
}
};
/**
* @type {import("esbuild").BuildOptions}
*/
let buildsFinished = Promise.resolve();
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 = {
logLevel: "info",
bundle: true,
watch,
minify: !watch,
sourcemap: watch ? "inline" : "",
sourcemap: watch ? "inline" : "external",
legalComments: "linked",
banner,
plugins: [fileUrlPlugin, gitHashPlugin, gitRemotePlugin, stylePlugin],
plugins: [fileUrlPlugin, gitHashPlugin, gitRemotePlugin, stylePlugin, buildsFinishedPlugin],
external: ["~plugins", "~git-hash", "~git-remote", "/assets/*"],
inject: ["./scripts/build/inject/react.mjs"],
jsxFactory: "VencordCreateElement",
jsxFragment: "VencordFragment",
// Work around https://github.com/evanw/esbuild/issues/2460
tsconfig: "./scripts/build/tsconfig.esbuild.json"
};
jsx: "transform"
} 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: {
...process.env,
VENCORD_USER_DATA_DIR: BASE_DIR,
VENCORD_DIRECTORY: join(BASE_DIR, "dist/desktop"),
VENCORD_DEV_INSTALL: "1"
}
});

View file

@ -16,15 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* @param {string} filePath
* @returns {string | null}
*/
export function getPluginTarget(filePath) {
export function getPluginTarget(filePath: string) {
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(".");
return identiferBits.length === 1 ? null : identiferBits.at(-1);
}

View file

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

View file

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

View file

@ -26,14 +26,14 @@ import { IS_VANILLA } from "./utils/constants";
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
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
const asarPath = join(dirname(injectorPath), "..", asarName);
const asarPath = join(dirname(injectorPath), "..", "_app.asar");
const discordPkg = require(join(asarPath, "package.json"));
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
app.setAppPath(asarPath);
if (!IS_VANILLA) {
if (!IS_VANILLA && !isLegacyNonAsarVencord) {
const settings = RendererSettings.store;
// Repatch after host updates on Windows
if (process.platform === "win32") {
@ -71,7 +71,7 @@ if (!IS_VANILLA) {
constructor(options: BrowserWindowConstructorOptions) {
if (options?.webPreferences?.preload && options.title) {
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;
// work around discord unloading when in background
options.webPreferences.backgroundThrottling = false;
@ -157,5 +157,7 @@ if (!IS_VANILLA) {
console.log("[Vencord] Running in vanilla mode. Not loading Vencord");
}
console.log("[Vencord] Loading original Discord app.asar");
require(require.main!.filename);
if (!isLegacyNonAsarVencord) {
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/>.
*/
export const VENCORD_FILES = [
IS_DISCORD_DESKTOP ? "patcher.js" : "vencordDesktopMain.js",
IS_DISCORD_DESKTOP ? "preload.js" : "vencordDesktopPreload.js",
IS_DISCORD_DESKTOP ? "renderer.js" : "vencordDesktopRenderer.js",
IS_DISCORD_DESKTOP ? "renderer.css" : "vencordDesktopRenderer.css",
];
export const ASAR_FILE = IS_VESKTOP
? "vesktop.asar"
: "desktop.asar";
export function serializeErrors(func: (...args: any[]) => any) {
return async function () {

View file

@ -16,20 +16,27 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { isLegacyNonAsarVencord } from "@main/patcher";
import { IpcEvents } from "@shared/IpcEvents";
import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent";
import { ipcMain } from "electron";
import { writeFile } from "fs/promises";
import { app, dialog, ipcMain } from "electron";
import {
existsSync as originalExistsSync,
renameSync as originalRenameSync,
writeFileSync as originalWriteFileSync,
} from "original-fs";
import { join } from "path";
import gitHash from "~git-hash";
import gitRemote from "~git-remote";
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}`;
let PendingUpdates = [] as [string, string][];
let PendingUpdate: string | null = null;
let hasUpdateToApplyOnQuit = false;
async function githubGet(endpoint: string) {
return get(API_BASE + endpoint, {
@ -65,22 +72,22 @@ async function fetchUpdates() {
if (hash === gitHash)
return false;
data.assets.forEach(({ name, browser_download_url }) => {
if (VENCORD_FILES.some(s => name.startsWith(s))) {
PendingUpdates.push([name, browser_download_url]);
}
});
const asset = data.assets.find(a => a.name === ASAR_FILE);
PendingUpdate = asset.browser_download_url;
return true;
}
async function applyUpdates() {
await Promise.all(PendingUpdates.map(
async ([name, data]) => writeFile(
join(__dirname, name),
await get(data)
)
));
PendingUpdates = [];
if (!PendingUpdate) return true;
const data = await get(PendingUpdate);
originalWriteFileSync(__dirname + ".new", data);
hasUpdateToApplyOnQuit = true;
PendingUpdate = null;
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.UPDATE, serializeErrors(fetchUpdates));
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) {
return row.type === 5
? -UserAffinitiesStore.getUserAffinity(row.user.id)?.affinity ?? 0
? -(UserAffinitiesStore.getUserAffinity(row.user.id)?.affinity ?? 0)
: comparator(row);
},

View file

@ -28,7 +28,7 @@ contextBridge.exposeInMainWorld("VencordNative", VencordNative);
// Discord
if (location.protocol !== "data:") {
// #region cssInsert
const rendererCss = join(__dirname, IS_VESKTOP ? "vencordDesktopRenderer.css" : "renderer.css");
const rendererCss = join(__dirname, "renderer.css");
const style = document.createElement("style");
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}
* @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]]);
return proxyLazy<T>(factory, attempts);