More work on the base

* Exported utilities in the `common` module
* Added build tool to bundle the client.
* Make injector, preload, client run
* Add WIP inject script
* Add our own packaging util.
This commit is contained in:
Strencher 2023-01-02 02:51:05 +01:00
parent 73116fbd34
commit a4ed32bb4d
26 changed files with 709 additions and 24 deletions

View File

@ -2,8 +2,14 @@
"name": "betterdiscord",
"version": "2.0.0",
"description": "",
"main": "./packages/injector/dist/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "echo \"Error: no test specified\" && exit 1",
"build": "pnpm build:injector && pnpm build:preload && pnpm build:preload",
"build:injector": "node ./scripts/build.js prod injector",
"build:preload": "node ./scripts/build.js prod preload",
"build:client": "node ./scripts/build.js prod client",
"build:packager": "node ./scripts/build.js prod packager"
},
"keywords": [],
"author": "",
@ -12,6 +18,8 @@
"@typescript-eslint/eslint-plugin": "^5.38.1",
"@typescript-eslint/parser": "^5.38.1",
"eslint": "^8.24.0",
"typescript": "^4.8.4"
"typescript": "^4.8.4",
"esbuild": "^0.15.10",
"esbuild-plugin-replace": "^1.3.0"
}
}

View File

@ -2,8 +2,14 @@
"name": "@betterdiscord/client",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"main": "dist/index.js",
"scripts": {
"build": "esbuild src/index.ts --bundle --outfile=dist/index.js"
},
"dependencies": {
"@betterdiscord/common": "workspace:*"
},
"devDependencies": {
"esbuild": "^0.15.10"
}
}
}

View File

@ -0,0 +1 @@
console.log("Hello from @betterdiscord/client");

View File

@ -1,4 +1,5 @@
{
"name": "@betterdiscord/common",
"private": true
}
"private": true,
"main": "src/index.ts"
}

View File

@ -0,0 +1 @@
export * as IPCEvents from "./ipc";

View File

@ -0,0 +1 @@
export const GET_PRELOAD = "BD_GET_PRELOAD";

View File

@ -0,0 +1,2 @@
export * as Utilities from "./utils";
export * from "./constants";

View File

@ -34,4 +34,4 @@ export default function findInTree(tree: ObjectLiteral | null, searchFilter: Tre
}
}
return tempReturn;
}
}

View File

@ -0,0 +1,7 @@
export {default as clone} from "./clone";
export {default as debounce} from "./debounce";
export {default as extend} from "./extend";
export {default as findInTree} from "./findintree";
export {default as formatString} from "./format";
export {default as Logger} from "./logger";
export {default as memoizeObject} from "./memoize";

View File

@ -2,8 +2,15 @@
"name": "@betterdiscord/injector",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"main": "dist/index.js",
"scripts": {
"build": "esbuild src/index.ts --bundle --outfile=dist/index.js"
},
"dependencies": {
"@betterdiscord/common": "workspace:*"
},
"devDependencies": {
"esbuild": "^0.15.10",
"standalone-electron-types": "^1.0.0"
}
}
}

View File

@ -0,0 +1,8 @@
/// <reference types="standalone-electron-types" />
import BrowserWindowPatcher from "./patchers/browserwindow";
import "./ipc";
if (!process.argv.includes("--vanilla")) {
BrowserWindowPatcher.apply();
}

View File

@ -0,0 +1 @@
import "./preload.ts";

View File

@ -0,0 +1,6 @@
import {ipcMain as IPC, BrowserWindow} from "electron/main";
import {IPCEvents} from "@betterdiscord/common";
IPC.on(IPCEvents.GET_PRELOAD, event => {
event.returnValue = (<any>BrowserWindow.fromWebContents(event.sender))?.__originalPreload ?? "NOT_FOUND";
});

View File

@ -0,0 +1,35 @@
import electron from "electron/main";
import path from "path";
const preloadPath = path.resolve(...<string[]>[
__dirname, "..",
process.env.DEVELOPMENT && "..",
"preload", "dist", "index.js"
].filter(Boolean));
class BrowserWindow extends electron.BrowserWindow {
__originalPreload: string = "";
constructor(options: Electron.BrowserWindowConstructorOptions) {
// @ts-expect-error
if (!options || !options.webPreferences || !options.webPreferences.preload || !options.title) super(options);
else {
const originalPreload = <string>options.webPreferences.preload;
options.webPreferences.preload = preloadPath;
super(options);
this.__originalPreload = originalPreload;
}
}
}
export default class BrowserWindowPatcher {
public static apply(): void {
const electronPath = require.resolve("electron");
delete require.cache[electronPath]?.exports;
(<any>require.cache[electronPath]).exports = {...electron, BrowserWindow};
}
}

View File

@ -0,0 +1,8 @@
{
"name": "packager",
"description": "",
"main": "dist/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
}
}

View File

@ -0,0 +1,80 @@
import fs from "fs";
import path from "path";
import Bundle, {ArchiveEntry} from "./modules/bundle";
import {Decoder, Encoder, concatBuffers, panic} from "./modules/utils";
export const pattern = [0x539, 0x45];
export const extractContents = (input: Buffer | Uint8Array) => {
const view = new DataView(input.buffer);
if (view.getUint32(0) !== pattern[0] || view.getUint32(4) !== pattern[1]) {
throw "Not a valid BD package. (pattern does not match)";
}
const headLength = view.getUint32(8);
const header = Decoder.decode(input.slice(0x10, 0x10 + headLength).buffer);
const head = JSON.parse(header);
return new Bundle(input.slice(0x10), head, headLength);
};
export const create = (location: string) => {
if (!fs.existsSync(location)) return null;
const bundle = (sub: string, list: ArchiveEntry, {files = [] as Buffer[], offset = 0} = {}): [Buffer[], ArchiveEntry, number] | undefined => {
const stats = fs.statSync(sub);
if (stats.isFile()) {
const content = fs.readFileSync(sub);
files.push(content);
list.files[path.basename(sub)] = {
size: String(content.length),
offset: String(offset)
}
offset += content.length;
return [files, list, offset];
}
if (stats.isDirectory()) {
if (sub !== location) {
list = (<ArchiveEntry["files"]><unknown>list)[path.basename(sub)] = {files: {}};
}
for (const file of fs.readdirSync(sub, {withFileTypes: true})) {
const [, , newOffset] = bundle(path.join(sub, file.name), list, {
files,
offset
}) as [never, never, number];
offset = newOffset;
}
return [files, list, offset];
}
panic(`Unknown dirent ${sub}`);
};
const head = {files: {}};
const [files] = bundle(location, head) as Array<Buffer[]>;
const headerBuf = Encoder.encode(JSON.stringify(head));
const fileBuf = concatBuffers(files.flat());
const buffer = new Uint8Array(0x10);
const view = new DataView(buffer.buffer);
view.setUint32(0, pattern[0]);
view.setUint32(4, pattern[1]);
view.setUint32(8, headerBuf.length);
return concatBuffers([
buffer,
headerBuf,
fileBuf
]);
};

View File

@ -0,0 +1,65 @@
import {Decoder} from "./utils";
export type ArchiveFile = {
size: string,
offset: string
};
export type ArchiveEntry = {
files: {[key: string]: ArchiveEntry | ArchiveFile}
};
export type ArchiveHeader = {
files: ArchiveEntry
};
export default class Bundle {
buffer: Buffer | Uint8Array;
offsets: ArchiveEntry;
bodyOffset = 0;
constructor(buf: Buffer | Uint8Array, header: ArchiveEntry, bodyOffset: number) {
this.bodyOffset = bodyOffset;
this.offsets = header;
this.buffer = buf;
}
readDir(dest: string, raw?: false): string[];
readDir(dest: string, raw?: true): ArchiveEntry["files"];
readDir(dest = "", raw?: boolean): ArchiveEntry["files"] | string[] {
const split = dest.split(/\\|\//);
let current = this.offsets.files;
if (split.length > 1) for (const item of split) {
if (!current) throw "No such file or directory.";
if (item === ".") continue;
const keys = Object.keys(current);
const target = keys.find(k => k.toLowerCase() === item.toLowerCase());
if (!target || !(<ArchiveEntry>current[target]).files) throw "ENOENT";
current = (<ArchiveEntry>current[target]).files;
}
return raw ? current : Object.keys(current);
}
readFile(dest = "", string?: boolean) {
const split = dest.split(/\\|\//);
const last = <string>split.pop();
const file = <ArchiveFile>this.readDir(split.join("/"), true)[last];
if (!file) throw "No such file found.";
const offset = this.bodyOffset + Number(file.offset);
return string
? Decoder.decode(this.buffer.subarray(offset, offset + Number(file.size)).buffer)
: this.buffer.subarray(offset, offset + Number(file.size));
}
hasFile(dest: string) {
return !!this.readFile(dest);
}
}

View File

@ -0,0 +1,24 @@
export const Decoder = new TextDecoder();
export const Encoder = new TextEncoder();
export const panic = (message: any) => {throw "PANIC: " + message;};
export const concatBuffers = (list: (Buffer | Uint8Array)[], length?: number) => {
if (typeof(length) === "undefined") {
length = 0;
for (const item of list) {
if (!item.length) continue;
length += item.length;
}
}
const buffer = new Uint8Array(length);
let pos = 0;
for (const item of list) {
buffer.set(item, pos);
pos += item.length;
}
return buffer;
}

View File

@ -2,8 +2,14 @@
"name": "@betterdiscord/preload",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"main": "dist/index.js",
"scripts": {
"build": "esbuild src/index.ts --bundle --outfile=dist/index.js"
},
"dependencies": {
"@betterdiscord/common": "workspace:*"
},
"devDependencies": {
"esbuild": "^0.15.10"
}
}
}

View File

@ -0,0 +1,9 @@
import Client from "./modules/client";
import DiscordPreload from "./modules/preload";
console.log("Hello from @betterdiscord/preload.");
Client.load();
DiscordPreload.load();
window.require = require;

View File

@ -0,0 +1,15 @@
import {webFrame} from "electron/renderer";
import path from "path";
import fs from "fs";
export default class Client {
public static load(): void {
const clientPath = path.resolve(...<string[]>[
__dirname, "..",
process.env.DEVELOPMENT && "..",
"client", "dist", "index.js"
].filter(Boolean));
webFrame.top?.executeJavaScript(fs.readFileSync(clientPath, "utf8"));
}
}

View File

@ -0,0 +1,14 @@
import {ipcRenderer as IPC} from "electron/renderer";
import {IPCEvents} from "@betterdiscord/common";
export default class DiscordPreload {
public static load(): void {
const originalPreload = IPC.sendSync(IPCEvents.GET_PRELOAD);
if (originalPreload !== "NOT_FOUND") {
require(originalPreload);
} else {
console.error("[BetterDiscord~Preload] Original preload.js wasn't found.");
}
}
}

View File

@ -1,21 +1,77 @@
lockfileVersion: 5.4
specifiers:
'@types/node': ^18.7.23
'@typescript-eslint/eslint-plugin': ^5.38.1
'@typescript-eslint/parser': ^5.38.1
eslint: ^8.24.0
typescript: ^4.8.4
importers:
devDependencies:
'@types/node': 18.7.23
'@typescript-eslint/eslint-plugin': 5.38.1_c7qepppml3d4ahu5cnfwqe6ltq
'@typescript-eslint/parser': 5.38.1_ypn2ylkkyfa5i233caldtndbqa
eslint: 8.24.0
typescript: 4.8.4
.:
specifiers:
'@types/node': ^18.7.23
'@typescript-eslint/eslint-plugin': ^5.38.1
'@typescript-eslint/parser': ^5.38.1
esbuild: ^0.15.10
esbuild-plugin-replace: ^1.3.0
eslint: ^8.24.0
typescript: ^4.8.4
devDependencies:
'@types/node': 18.7.23
'@typescript-eslint/eslint-plugin': 5.38.1_c7qepppml3d4ahu5cnfwqe6ltq
'@typescript-eslint/parser': 5.38.1_ypn2ylkkyfa5i233caldtndbqa
esbuild: 0.15.10
esbuild-plugin-replace: 1.3.0
eslint: 8.24.0
typescript: 4.8.4
packages/client:
specifiers:
'@betterdiscord/common': workspace:*
esbuild: ^0.15.10
dependencies:
'@betterdiscord/common': link:../common
devDependencies:
esbuild: 0.15.10
packages/common:
specifiers: {}
packages/injector:
specifiers:
'@betterdiscord/common': workspace:*
esbuild: ^0.15.10
standalone-electron-types: ^1.0.0
dependencies:
'@betterdiscord/common': link:../common
devDependencies:
esbuild: 0.15.10
standalone-electron-types: 1.0.0
packages/preload:
specifiers:
'@betterdiscord/common': workspace:*
esbuild: ^0.15.10
dependencies:
'@betterdiscord/common': link:../common
devDependencies:
esbuild: 0.15.10
packages:
/@esbuild/android-arm/0.15.10:
resolution: {integrity: sha512-FNONeQPy/ox+5NBkcSbYJxoXj9GWu8gVGJTVmUyoOCKQFDTrHVKgNSzChdNt0I8Aj/iKcsDf2r9BFwv+FSNUXg==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-loong64/0.15.10:
resolution: {integrity: sha512-w0Ou3Z83LOYEkwaui2M8VwIp+nLi/NA60lBLMvaJ+vXVMcsARYdEzLNE7RSm4+lSg4zq4d7fAVuzk7PNQ5JFgg==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@eslint/eslintrc/1.3.2:
resolution: {integrity: sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -82,6 +138,10 @@ packages:
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
dev: true
/@types/node/18.11.18:
resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==}
dev: true
/@types/node/18.7.23:
resolution: {integrity: sha512-DWNcCHolDq0ZKGizjx2DZjR/PqsYwAcYUJmfMWqtVU2MBMG5Mo+xFZrhGId5r/O5HOuMPyQEcM6KUBp5lBZZBg==}
dev: true
@ -341,6 +401,222 @@ packages:
esutils: 2.0.3
dev: true
/esbuild-android-64/0.15.10:
resolution: {integrity: sha512-UI7krF8OYO1N7JYTgLT9ML5j4+45ra3amLZKx7LO3lmLt1Ibn8t3aZbX5Pu4BjWiqDuJ3m/hsvhPhK/5Y/YpnA==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-android-arm64/0.15.10:
resolution: {integrity: sha512-EOt55D6xBk5O05AK8brXUbZmoFj4chM8u3riGflLa6ziEoVvNjRdD7Cnp82NHQGfSHgYR06XsPI8/sMuA/cUwg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-64/0.15.10:
resolution: {integrity: sha512-hbDJugTicqIm+WKZgp208d7FcXcaK8j2c0l+fqSJ3d2AzQAfjEYDRM3Z2oMeqSJ9uFxyj/muSACLdix7oTstRA==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-arm64/0.15.10:
resolution: {integrity: sha512-M1t5+Kj4IgSbYmunf2BB6EKLkWUq+XlqaFRiGOk8bmBapu9bCDrxjf4kUnWn59Dka3I27EiuHBKd1rSO4osLFQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-64/0.15.10:
resolution: {integrity: sha512-KMBFMa7C8oc97nqDdoZwtDBX7gfpolkk6Bcmj6YFMrtCMVgoU/x2DI1p74DmYl7CSS6Ppa3xgemrLrr5IjIn0w==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-arm64/0.15.10:
resolution: {integrity: sha512-m2KNbuCX13yQqLlbSojFMHpewbn8wW5uDS6DxRpmaZKzyq8Dbsku6hHvh2U+BcLwWY4mpgXzFUoENEf7IcioGg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-32/0.15.10:
resolution: {integrity: sha512-guXrwSYFAvNkuQ39FNeV4sNkNms1bLlA5vF1H0cazZBOLdLFIny6BhT+TUbK/hdByMQhtWQ5jI9VAmPKbVPu1w==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-64/0.15.10:
resolution: {integrity: sha512-jd8XfaSJeucMpD63YNMO1JCrdJhckHWcMv6O233bL4l6ogQKQOxBYSRP/XLWP+6kVTu0obXovuckJDcA0DKtQA==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm/0.15.10:
resolution: {integrity: sha512-6N8vThLL/Lysy9y4Ex8XoLQAlbZKUyExCWyayGi2KgTBelKpPgj6RZnUaKri0dHNPGgReJriKVU6+KDGQwn10A==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm64/0.15.10:
resolution: {integrity: sha512-GByBi4fgkvZFTHFDYNftu1DQ1GzR23jws0oWyCfhnI7eMOe+wgwWrc78dbNk709Ivdr/evefm2PJiUBMiusS1A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-mips64le/0.15.10:
resolution: {integrity: sha512-BxP+LbaGVGIdQNJUNF7qpYjEGWb0YyHVSKqYKrn+pTwH/SiHUxFyJYSP3pqkku61olQiSBnSmWZ+YUpj78Tw7Q==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-ppc64le/0.15.10:
resolution: {integrity: sha512-LoSQCd6498PmninNgqd/BR7z3Bsk/mabImBWuQ4wQgmQEeanzWd5BQU2aNi9mBURCLgyheuZS6Xhrw5luw3OkQ==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-riscv64/0.15.10:
resolution: {integrity: sha512-Lrl9Cr2YROvPV4wmZ1/g48httE8z/5SCiXIyebiB5N8VT7pX3t6meI7TQVHw/wQpqP/AF4SksDuFImPTM7Z32Q==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-s390x/0.15.10:
resolution: {integrity: sha512-ReP+6q3eLVVP2lpRrvl5EodKX7EZ1bS1/z5j6hsluAlZP5aHhk6ghT6Cq3IANvvDdscMMCB4QEbI+AjtvoOFpA==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-netbsd-64/0.15.10:
resolution: {integrity: sha512-iGDYtJCMCqldMskQ4eIV+QSS/CuT7xyy9i2/FjpKvxAuCzrESZXiA1L64YNj6/afuzfBe9i8m/uDkFHy257hTw==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-openbsd-64/0.15.10:
resolution: {integrity: sha512-ftMMIwHWrnrYnvuJQRJs/Smlcb28F9ICGde/P3FUTCgDDM0N7WA0o9uOR38f5Xe2/OhNCgkjNeb7QeaE3cyWkQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-plugin-replace/1.3.0:
resolution: {integrity: sha512-i9v5FDfKUaxzpCLn+avq3I6lxJDL5616/tzP93/GFAI7REFwkMTy/pilEIvqlH8RTVpjlwJDOqUOYvJOf5jnXg==}
dependencies:
magic-string: 0.25.9
dev: true
/esbuild-sunos-64/0.15.10:
resolution: {integrity: sha512-mf7hBL9Uo2gcy2r3rUFMjVpTaGpFJJE5QTDDqUFf1632FxteYANffDZmKbqX0PfeQ2XjUDE604IcE7OJeoHiyg==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-32/0.15.10:
resolution: {integrity: sha512-ttFVo+Cg8b5+qHmZHbEc8Vl17kCleHhLzgT8X04y8zudEApo0PxPg9Mz8Z2cKH1bCYlve1XL8LkyXGFjtUYeGg==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-64/0.15.10:
resolution: {integrity: sha512-2H0gdsyHi5x+8lbng3hLbxDWR7mKHWh5BXZGKVG830KUmXOOWFE2YKJ4tHRkejRduOGDrBvHBriYsGtmTv3ntA==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-arm64/0.15.10:
resolution: {integrity: sha512-S+th4F+F8VLsHLR0zrUcG+Et4hx0RKgK1eyHc08kztmLOES8BWwMiaGdoW9hiXuzznXQ0I/Fg904MNbr11Nktw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild/0.15.10:
resolution: {integrity: sha512-N7wBhfJ/E5fzn/SpNgX+oW2RLRjwaL8Y0ezqNqhjD6w0H2p0rDuEz2FKZqpqLnO8DCaWumKe8dsC/ljvVSSxng==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.15.10
'@esbuild/linux-loong64': 0.15.10
esbuild-android-64: 0.15.10
esbuild-android-arm64: 0.15.10
esbuild-darwin-64: 0.15.10
esbuild-darwin-arm64: 0.15.10
esbuild-freebsd-64: 0.15.10
esbuild-freebsd-arm64: 0.15.10
esbuild-linux-32: 0.15.10
esbuild-linux-64: 0.15.10
esbuild-linux-arm: 0.15.10
esbuild-linux-arm64: 0.15.10
esbuild-linux-mips64le: 0.15.10
esbuild-linux-ppc64le: 0.15.10
esbuild-linux-riscv64: 0.15.10
esbuild-linux-s390x: 0.15.10
esbuild-netbsd-64: 0.15.10
esbuild-openbsd-64: 0.15.10
esbuild-sunos-64: 0.15.10
esbuild-windows-32: 0.15.10
esbuild-windows-64: 0.15.10
esbuild-windows-arm64: 0.15.10
dev: true
/escape-string-regexp/4.0.0:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
@ -683,6 +959,12 @@ packages:
yallist: 4.0.0
dev: true
/magic-string/0.25.9:
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
dependencies:
sourcemap-codec: 1.4.8
dev: true
/merge2/1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@ -841,6 +1123,17 @@ packages:
engines: {node: '>=8'}
dev: true
/sourcemap-codec/1.4.8:
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
deprecated: Please use @jridgewell/sourcemap-codec instead
dev: true
/standalone-electron-types/1.0.0:
resolution: {integrity: sha512-0HOi/tlTz3mjWhsAz4uRbpQcHMZ+ifj1JzWW9nugykOHClBBG77ps8QinrzX1eow4Iw2pnC+RFaSYRgufF4BOg==}
dependencies:
'@types/node': 18.11.18
dev: true
/strip-ansi/6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}

43
scripts/build.js Normal file
View File

@ -0,0 +1,43 @@
const {build} = require("esbuild");
const {replace} = require("esbuild-plugin-replace");
const path = require("path");
const [mode, packagesToBuild] = process.argv.slice(2);
const isDevelopment = mode !== "prod";
const packages = packagesToBuild ? packagesToBuild.split(",") : ["packager", "injector", "preload", "client"];
const buildPackage = id => {
const base = path.resolve(process.cwd(), "packages", id);
/**@type {import("esbuild").BuildOptions} */
const config = {
entryPoints: [path.resolve(base, "src", "index.ts")],
bundle: true,
external: require("module").builtinModules.concat("electron"),
outfile: path.resolve(base, "dist", "index.js"),
format: id === "client" ? "iife" : "cjs",
watch: isDevelopment,
minify: !isDevelopment,
plugins: [
replace({
"process.env.DEVELOPMENT": JSON.stringify(isDevelopment)
})
],
keepNames: true
};
return build(config);
};
const label = `Built ${packages.length} packages in`;
console.clear();
console.time(label);
Promise.all(packages.map(buildPackage))
.then(() => {
console.timeEnd(label);
})
.catch(err => {
console.error("Build failed:", err);
});

41
scripts/inject.js Normal file
View File

@ -0,0 +1,41 @@
const fs = require("fs");
const path = require("path");
// TODO: Make it work with release builds.
const args = process.argv;
const releaseInput = /*useBdRelease ? args[3] && args[3].toLowerCase() : */args[2] && args[2].toLowerCase();
const release = releaseInput === "canary" ? "Discord Canary" : releaseInput === "ptb" ? "Discord PTB" : "Discord";
const bdPath = /*useBdRelease ?*/ path.resolve(__dirname, "..")/* : path.resolve(__dirname, "..", "dist")*/;
const discordPath = (function() {
let resourcePath = "";
if (process.platform === "win32") {
const basedir = path.join(process.env.LOCALAPPDATA, release.replace(/ /g, ""));
if (!fs.existsSync(basedir)) throw new Error(`Cannot find directory for ${release}`);
const version = fs.readdirSync(basedir).filter(f => fs.lstatSync(path.join(basedir, f)).isDirectory() && f.split(".").length > 1).sort().reverse()[0];
// To account for discord_desktop_core-1 or any other number
const coreWrap = fs.readdirSync(path.join(basedir, version, "modules")).filter(e => e.indexOf("discord_desktop_core") === 0).sort().reverse()[0];
resourcePath = path.join(basedir, version, "modules", coreWrap, "discord_desktop_core");
}
else {
let userData = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : path.join(process.env.HOME, ".config");
if (process.platform === "darwin") userData = path.join(process.env.HOME, "Library", "Application Support");
const basedir = path.join(userData, release.toLowerCase().replace(" ", ""));
if (!fs.existsSync(basedir)) return "";
const version = fs.readdirSync(basedir).filter(f => fs.lstatSync(path.join(basedir, f)).isDirectory() && f.split(".").length > 1).sort().reverse()[0];
if (!version) return "";
resourcePath = path.join(basedir, version, "modules", "discord_desktop_core");
}
if (fs.existsSync(resourcePath)) return resourcePath;
return "";
})();
if (!fs.existsSync(discordPath)) throw new Error(`Cannot find directory for ${release}`);
const indexJs = path.join(discordPath, "index.js");
if (fs.existsSync(indexJs)) fs.unlinkSync(indexJs);
fs.writeFileSync(indexJs, `require(${JSON.stringify(bdPath)});\nmodule.exports = require("./core.asar");`);
console.log(`Injection successful, please restart ${release}.`);

View File

@ -12,6 +12,9 @@
/* Language and Environment */
"target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"module": "esNext",
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
"jsx": "react-jsx", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
@ -42,4 +45,4 @@
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
// "include": ["./src/types.ts"]
}
}