#!/usr/bin/node /* * Vencord, a modification for Discord's desktop app * Copyright (c) 2022 Vendicated and contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ 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 { 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: 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: String(BUILD_TIMESTAMP) }; 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); const nodeCommonOpts = { ...commonOpts, format: "cjs", platform: "node", target: ["esnext"], external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external], define: defines } satisfies BuildOptions; const sourceMapFooter = (s: string) => watch ? "" : `//# sourceMappingURL=vencord://${s}.js.map`; const sourcemap = watch ? "inline" : "external"; const globNativesPlugin: Plugin = { name: "glob-natives-plugin", setup: build => { const filter = /^~pluginNatives$/; build.onResolve({ filter }, args => { return { namespace: "import-natives", path: args.path }; }); build.onLoad({ filter, namespace: "import-natives" }, async () => { const pluginDirs = ["plugins", "userplugins"]; let code = ""; let natives = "\n"; let i = 0; for (const dir of pluginDirs) { const dirPath = join("src", dir); if (!await exists(dirPath)) continue; const plugins = await readdir(dirPath, { withFileTypes: true }); for (const file of plugins) { const fileName = file.name; const nativePath = join(dirPath, fileName, "native.ts"); const indexNativePath = join(dirPath, fileName, "native/index.ts"); if (!(await exists(nativePath)) && !(await exists(indexNativePath))) continue; const pluginName = await resolvePluginName(dirPath, file); const mod = `p${i}`; code += `import * as ${mod} from "./${dir}/${fileName}/native";\n`; natives += `${JSON.stringify(pluginName)}:${mod},\n`; i++; } } code += `export default {${natives}};`; return { contents: code, resolveDir: "./src" }; }); } }; await Promise.all([ // Discord Desktop main & renderer & preload addBuild({ ...nodeCommonOpts, entryPoints: ["src/main/index.ts"], outfile: "dist/desktop/patcher.js", footer: { js: "//# sourceURL=VencordPatcher\n" + sourceMapFooter("patcher") }, sourcemap, define: { ...defines, IS_DISCORD_DESKTOP: "true", IS_VESKTOP: "false" }, plugins: [ ...nodeCommonOpts.plugins, globNativesPlugin ] }), addBuild({ ...commonOpts, entryPoints: ["src/Vencord.ts"], outfile: "dist/desktop/renderer.js", format: "iife", target: ["esnext"], footer: { js: "//# sourceURL=VencordRenderer\n" + sourceMapFooter("renderer") }, globalName: "Vencord", sourcemap, plugins: [ globPlugins("discordDesktop"), ...commonOpts.plugins ], define: { ...defines, IS_DISCORD_DESKTOP: "true", IS_VESKTOP: "false" } }), addBuild({ ...nodeCommonOpts, entryPoints: ["src/preload.ts"], outfile: "dist/desktop/preload.js", footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") }, sourcemap, define: { ...defines, IS_DISCORD_DESKTOP: "true", IS_VESKTOP: "false" } }), // Vencord Desktop main & renderer & preload addBuild({ ...nodeCommonOpts, entryPoints: ["src/main/index.ts"], outfile: "dist/vesktop/main.js", footer: { js: "//# sourceURL=VencordMain\n" + sourceMapFooter("main") }, sourcemap, define: { ...defines, IS_DISCORD_DESKTOP: "false", IS_VESKTOP: "true" }, plugins: [ ...nodeCommonOpts.plugins, globNativesPlugin ] }), addBuild({ ...commonOpts, entryPoints: ["src/Vencord.ts"], outfile: "dist/vesktop/renderer.js", format: "iife", target: ["esnext"], footer: { js: "//# sourceURL=VencordRenderer\n" + sourceMapFooter("renderer") }, globalName: "Vencord", sourcemap, plugins: [ globPlugins("vencordDesktop"), ...commonOpts.plugins ], define: { ...defines, IS_DISCORD_DESKTOP: "false", IS_VESKTOP: "true" } }), addBuild({ ...nodeCommonOpts, entryPoints: ["src/preload.ts"], outfile: "dist/vesktop/preload.js", footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") }, sourcemap, define: { ...defines, IS_DISCORD_DESKTOP: "false", IS_VESKTOP: "true" } }), ]); 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")') ]); }