From d0c3f5088050b32dfa92411bbcf5ba6c9afb7c16 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Wed, 8 May 2024 21:54:21 +0530 Subject: [PATCH 01/12] idk --- bin/index.mts | 26 ++++++++++++++++++ src/main/ipc/plugins.ts | 46 ++++++++++++++++++++++++-------- src/preload.ts | 14 ++++++++++ src/renderer/managers/plugins.ts | 1 + src/types/addon.ts | 1 + src/types/index.ts | 1 + 6 files changed, 78 insertions(+), 11 deletions(-) diff --git a/bin/index.mts b/bin/index.mts index 05570f840..848066c42 100755 --- a/bin/index.mts +++ b/bin/index.mts @@ -338,6 +338,18 @@ async function buildPlugin({ watch, noInstall, production, noReload, addon }: Ar }; }); + build.onResolve( + { filter: new RegExp(`.*?${manifest.preload.split("/").pop().replace(/\..+/, "")}`) }, + (args) => { + if (args.kind !== "import-statement") return undefined; + + return { + path: `replugged/plugins/preload[${manifest.id}]`, + namespace: "replugged", + }; + }, + ); + build.onResolve({ filter: /^react$/ }, (args) => { if (args.kind !== "import-statement") return undefined; @@ -425,6 +437,20 @@ async function buildPlugin({ watch, noInstall, production, noReload, addon }: Ar manifest.plaintextPatches = "plaintextPatches.js"; } + if ("preload" in manifest) { + targets.push( + esbuild.context( + overwrites({ + ...common, + platform: "node", + entryPoints: [path.join(folderPath, manifest.preload)], + outfile: `${distPath}/preload.js`, + }), + ), + ); + + manifest.preload = "preload.js"; + } if (!existsSync(distPath)) mkdirSync(distPath, { recursive: true }); diff --git a/src/main/ipc/plugins.ts b/src/main/ipc/plugins.ts index 67b9bc884..f60895a84 100644 --- a/src/main/ipc/plugins.ts +++ b/src/main/ipc/plugins.ts @@ -11,7 +11,7 @@ import { RepluggedIpcChannels, type RepluggedPlugin } from "../../types"; import { plugin } from "../../types/addon"; import type { Dirent, Stats } from "fs"; import { CONFIG_PATHS } from "src/util.mjs"; - +import { getSetting } from "./settings"; const PLUGINS_DIR = CONFIG_PATHS.plugins; export const isFileAPlugin = (f: Dirent | Stats, name: string): boolean => { @@ -49,16 +49,7 @@ async function getPlugin(pluginName: string): Promise { return data; } -ipcMain.handle( - RepluggedIpcChannels.GET_PLUGIN, - async (_, pluginName: string): Promise => { - try { - return await getPlugin(pluginName); - } catch {} - }, -); - -ipcMain.handle(RepluggedIpcChannels.LIST_PLUGINS, async (): Promise => { +async function listPlugins(): Promise { const plugins = []; const pluginDirs = ( @@ -88,8 +79,41 @@ ipcMain.handle(RepluggedIpcChannels.LIST_PLUGINS, async (): Promise => { + try { + return await getPlugin(pluginName); + } catch {} + }, +); + +ipcMain.handle(RepluggedIpcChannels.LIST_PLUGINS, async (): Promise => { + return await listPlugins(); }); +ipcMain.handle( + RepluggedIpcChannels.LIST_PLUGINS_PRELOAD, + async (): Promise> => { + const disabled = await getSetting("dev.replugged.Settings", "disabled", []); + const plugins = await listPlugins(); + const pluginPreloadEntries = plugins + .map((p) => { + if (!p.manifest.preload) return [p.manifest.id, null]; + const preloadPath = join(PLUGINS_DIR, p.path, p.manifest.preload); + if (!preloadPath.startsWith(`${PLUGINS_DIR}${sep}`)) { + // Ensure file changes are restricted to the base path + throw new Error("Invalid plugin name"); + } + return [p.manifest.id, preloadPath]; + }) + .filter(([id, preload]) => preload && !disabled.includes(id!)); + return Object.fromEntries(pluginPreloadEntries); + }, +); + ipcMain.handle(RepluggedIpcChannels.UNINSTALL_PLUGIN, async (_, pluginName: string) => { const pluginPath = join(PLUGINS_DIR, pluginName); if (!pluginPath.startsWith(`${PLUGINS_DIR}${sep}`)) { diff --git a/src/preload.ts b/src/preload.ts index 1a611af16..b3505e586 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -22,6 +22,19 @@ let version = ""; void ipcRenderer.invoke(RepluggedIpcChannels.GET_REPLUGGED_VERSION).then((v) => { version = v; }); +const pluginPreloads = {} as Record unknown>>; + +void ipcRenderer.invoke(RepluggedIpcChannels.LIST_PLUGINS_PRELOAD).then((preloadList) => { + for (const id in preloadList) { + const pluginPreload = require(preloadList[id]); + const methods = {} as Record unknown>; + for (const [methodName, method] of Object.entries(pluginPreload)) { + // @ts-expect-error this is stupid + methods[methodName] = (...args) => method(...args); + } + pluginPreloads[id] = methods; + } +}); const RepluggedNative = { themes: { @@ -40,6 +53,7 @@ const RepluggedNative = { uninstall: async (pluginPath: string): Promise => ipcRenderer.invoke(RepluggedIpcChannels.UNINSTALL_PLUGIN, pluginPath), openFolder: () => ipcRenderer.send(RepluggedIpcChannels.OPEN_PLUGINS_FOLDER), + listPreload: () => pluginPreloads, }, updater: { diff --git a/src/renderer/managers/plugins.ts b/src/renderer/managers/plugins.ts index f2534400e..a3518c1ce 100644 --- a/src/renderer/managers/plugins.ts +++ b/src/renderer/managers/plugins.ts @@ -17,6 +17,7 @@ interface PluginWrapper extends RepluggedPlugin { * @hidden */ export const plugins = new Map(); +export const preload = RepluggedNative.plugins.listPreload(); const running = new Set(); const styleElements = new Map(); diff --git a/src/types/addon.ts b/src/types/addon.ts index b29ce9751..7a0227242 100644 --- a/src/types/addon.ts +++ b/src/types/addon.ts @@ -62,6 +62,7 @@ export const plugin = common.extend({ type: z.literal("replugged-plugin"), renderer: z.string().optional(), plaintextPatches: z.string().optional(), + preload: z.string().optional(), reloadRequired: z.boolean().optional(), }); diff --git a/src/types/index.ts b/src/types/index.ts index e614b2c92..03bffd73c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -22,6 +22,7 @@ export enum RepluggedIpcChannels { GET_THEME = "REPLUGGED_GET_THEME", UNINSTALL_THEME = "REPLUGGED_UNINSTALL_THEME", LIST_PLUGINS = "REPLUGGED_LIST_PLUGINS", + LIST_PLUGINS_PRELOAD = "REPLUGGED_LIST_PLUGINS_PRELOAD", GET_PLUGIN = "REPLUGGED_GET_PLUGIN", UNINSTALL_PLUGIN = "REPLUGGED_UNINSTALL_PLUGIN", REGISTER_RELOAD = "REPLUGGED_REGISTER_RELOAD", From aec0ece69ec7c76e283ceba6c73a52a288bcdaca Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Wed, 8 May 2024 21:56:42 +0530 Subject: [PATCH 02/12] optional chaining --- bin/index.mts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/index.mts b/bin/index.mts index 848066c42..88b128f02 100755 --- a/bin/index.mts +++ b/bin/index.mts @@ -339,7 +339,7 @@ async function buildPlugin({ watch, noInstall, production, noReload, addon }: Ar }); build.onResolve( - { filter: new RegExp(`.*?${manifest.preload.split("/").pop().replace(/\..+/, "")}`) }, + { filter: new RegExp(`.*?${manifest.preload?.split("/")?.pop()?.replace(/\..+/, "")}`) }, (args) => { if (args.kind !== "import-statement") return undefined; From bcfad2977ef244a6c4e7fd2d24ace9d682e92919 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Wed, 8 May 2024 22:28:30 +0530 Subject: [PATCH 03/12] meow --- src/preload.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/preload.ts b/src/preload.ts index b3505e586..c7721de2c 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -26,13 +26,8 @@ const pluginPreloads = {} as Record { for (const id in preloadList) { - const pluginPreload = require(preloadList[id]); - const methods = {} as Record unknown>; - for (const [methodName, method] of Object.entries(pluginPreload)) { - // @ts-expect-error this is stupid - methods[methodName] = (...args) => method(...args); - } - pluginPreloads[id] = methods; + // @ts-expect-error this is stupid + pluginPreloads[id] = require(preloadList[id]); } }); From 7ef077ea36a071d01b05e7a1482c7dbfee494a8d Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Thu, 9 May 2024 04:23:55 +0530 Subject: [PATCH 04/12] quotes --- bin/index.mts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/index.mts b/bin/index.mts index 88b128f02..fa319d2ee 100755 --- a/bin/index.mts +++ b/bin/index.mts @@ -344,7 +344,7 @@ async function buildPlugin({ watch, noInstall, production, noReload, addon }: Ar if (args.kind !== "import-statement") return undefined; return { - path: `replugged/plugins/preload[${manifest.id}]`, + path: `replugged/plugins/preload["${manifest.id}"]`, namespace: "replugged", }; }, @@ -443,6 +443,7 @@ async function buildPlugin({ watch, noInstall, production, noReload, addon }: Ar overwrites({ ...common, platform: "node", + format: "cjs", entryPoints: [path.join(folderPath, manifest.preload)], outfile: `${distPath}/preload.js`, }), From deb8abb73efcc91fd7ed58f02212210a11224ba3 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Thu, 9 May 2024 04:25:48 +0530 Subject: [PATCH 05/12] better ig --- bin/index.mts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/index.mts b/bin/index.mts index fa319d2ee..45af23011 100755 --- a/bin/index.mts +++ b/bin/index.mts @@ -339,7 +339,7 @@ async function buildPlugin({ watch, noInstall, production, noReload, addon }: Ar }); build.onResolve( - { filter: new RegExp(`.*?${manifest.preload?.split("/")?.pop()?.replace(/\..+/, "")}`) }, + { filter: new RegExp(`.*?/${manifest.preload?.split("/")?.pop()?.replace(/\..+/, "")}`) }, (args) => { if (args.kind !== "import-statement") return undefined; From 51164f687994820f8ed00c3d71c2e16ef951ea72 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Thu, 9 May 2024 04:41:47 +0530 Subject: [PATCH 06/12] lint --- src/preload.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/preload.ts b/src/preload.ts index c7721de2c..ceee096df 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -22,13 +22,14 @@ let version = ""; void ipcRenderer.invoke(RepluggedIpcChannels.GET_REPLUGGED_VERSION).then((v) => { version = v; }); -const pluginPreloads = {} as Record unknown>>; + +const pluginPreloads = {} as Record< + string, + Record unknown> | unknown +>; void ipcRenderer.invoke(RepluggedIpcChannels.LIST_PLUGINS_PRELOAD).then((preloadList) => { - for (const id in preloadList) { - // @ts-expect-error this is stupid - pluginPreloads[id] = require(preloadList[id]); - } + for (const id in preloadList) pluginPreloads[id] = require(preloadList[id]); }); const RepluggedNative = { From ab0a8e2b5794294b271cef79569dec937c124b20 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Thu, 9 May 2024 04:43:13 +0530 Subject: [PATCH 07/12] types --- src/renderer/managers/plugins.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/renderer/managers/plugins.ts b/src/renderer/managers/plugins.ts index a3518c1ce..def8ad43b 100644 --- a/src/renderer/managers/plugins.ts +++ b/src/renderer/managers/plugins.ts @@ -16,8 +16,11 @@ interface PluginWrapper extends RepluggedPlugin { /** * @hidden */ +export const preload = RepluggedNative.plugins.listPreload() as Record< + string, + Record unknown> +>; export const plugins = new Map(); -export const preload = RepluggedNative.plugins.listPreload(); const running = new Set(); const styleElements = new Map(); From 442dda470748e892421245c388c41ac8bbabeb19 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Fri, 26 Jul 2024 05:36:54 +0530 Subject: [PATCH 08/12] womp womp --- bin/index.mts | 12 ++++----- src/main/ipc/plugins.ts | 46 +++++++++++++++++++++----------- src/preload.ts | 22 +++++++++------ src/renderer/managers/plugins.ts | 5 +--- src/types/addon.ts | 2 +- src/types/index.ts | 2 +- 6 files changed, 54 insertions(+), 35 deletions(-) diff --git a/bin/index.mts b/bin/index.mts index 45af23011..81192aa99 100755 --- a/bin/index.mts +++ b/bin/index.mts @@ -339,12 +339,12 @@ async function buildPlugin({ watch, noInstall, production, noReload, addon }: Ar }); build.onResolve( - { filter: new RegExp(`.*?/${manifest.preload?.split("/")?.pop()?.replace(/\..+/, "")}`) }, + { filter: new RegExp(`.*?/${manifest.native?.split("/")?.pop()?.replace(/\..+/, "")}`) }, (args) => { if (args.kind !== "import-statement") return undefined; return { - path: `replugged/plugins/preload["${manifest.id}"]`, + path: `replugged/plugins/native["${manifest.id}"]`, namespace: "replugged", }; }, @@ -437,20 +437,20 @@ async function buildPlugin({ watch, noInstall, production, noReload, addon }: Ar manifest.plaintextPatches = "plaintextPatches.js"; } - if ("preload" in manifest) { + if ("native" in manifest) { targets.push( esbuild.context( overwrites({ ...common, platform: "node", format: "cjs", - entryPoints: [path.join(folderPath, manifest.preload)], - outfile: `${distPath}/preload.js`, + entryPoints: [path.join(folderPath, manifest.native)], + outfile: `${distPath}/native.js`, }), ), ); - manifest.preload = "preload.js"; + manifest.native = "native.js"; } if (!existsSync(distPath)) mkdirSync(distPath, { recursive: true }); diff --git a/src/main/ipc/plugins.ts b/src/main/ipc/plugins.ts index f60895a84..62941a066 100644 --- a/src/main/ipc/plugins.ts +++ b/src/main/ipc/plugins.ts @@ -14,6 +14,8 @@ import { CONFIG_PATHS } from "src/util.mjs"; import { getSetting } from "./settings"; const PLUGINS_DIR = CONFIG_PATHS.plugins; +let PluginIpcMappings: Record>; + export const isFileAPlugin = (f: Dirent | Stats, name: string): boolean => { return f.isDirectory() || (f.isFile() && extname(name) === ".asar"); }; @@ -95,22 +97,36 @@ ipcMain.handle(RepluggedIpcChannels.LIST_PLUGINS, async (): Promise> => { - const disabled = await getSetting("dev.replugged.Settings", "disabled", []); + RepluggedIpcChannels.LIST_PLUGINS_NATIVE, + async (): Promise>> => { + const disabled = await getSetting("plugins", "disabled", []); const plugins = await listPlugins(); - const pluginPreloadEntries = plugins - .map((p) => { - if (!p.manifest.preload) return [p.manifest.id, null]; - const preloadPath = join(PLUGINS_DIR, p.path, p.manifest.preload); - if (!preloadPath.startsWith(`${PLUGINS_DIR}${sep}`)) { - // Ensure file changes are restricted to the base path - throw new Error("Invalid plugin name"); - } - return [p.manifest.id, preloadPath]; - }) - .filter(([id, preload]) => preload && !disabled.includes(id!)); - return Object.fromEntries(pluginPreloadEntries); + + PluginIpcMappings ??= plugins.reduce((acc: Record>, plugin) => { + if (!plugin.manifest.native || disabled.includes(plugin.manifest.id)) return acc; + const nativePath = join(PLUGINS_DIR, plugin.path, plugin.manifest.native); + if (!nativePath.startsWith(`${PLUGINS_DIR}${sep}`)) { + // Ensure file changes are restricted to the base path + throw new Error("Invalid plugin name"); + } + const entries = Object.entries(require(nativePath)); + if (!entries.length) return acc; + + const mappings = (acc[plugin.manifest.id] = {} as Record); + + for (const [methodName, method] of entries) { + const key = `RPPlugin-Native_${plugin.manifest.id}_${methodName}`; + ipcMain.handle( + key, + (_, ...args) => + (method as (...args: unknown[]) => unknown)(...args) /* For easy type when importing */, + ); + mappings[methodName] = key; + } + return acc; + }, {}); + + return PluginIpcMappings; }, ); diff --git a/src/preload.ts b/src/preload.ts index ceee096df..2fa621ce6 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -23,13 +23,19 @@ void ipcRenderer.invoke(RepluggedIpcChannels.GET_REPLUGGED_VERSION).then((v) => version = v; }); -const pluginPreloads = {} as Record< - string, - Record unknown> | unknown ->; - -void ipcRenderer.invoke(RepluggedIpcChannels.LIST_PLUGINS_PRELOAD).then((preloadList) => { - for (const id in preloadList) pluginPreloads[id] = require(preloadList[id]); +const pluginNatives = {} as Record Promise>>; + +void ipcRenderer.invoke(RepluggedIpcChannels.LIST_PLUGINS_NATIVE).then((nativeList) => { + for (const pluginId in nativeList) { + const methods = nativeList[pluginId]; + const map = (pluginNatives[pluginId] = {} as Record< + string, + (...args: any[]) => Promise + >); + for (const methodName in methods) { + map[methodName] = (...args: any[]) => ipcRenderer.invoke(methods[methodName], ...args); + } + } }); const RepluggedNative = { @@ -49,7 +55,7 @@ const RepluggedNative = { uninstall: async (pluginPath: string): Promise => ipcRenderer.invoke(RepluggedIpcChannels.UNINSTALL_PLUGIN, pluginPath), openFolder: () => ipcRenderer.send(RepluggedIpcChannels.OPEN_PLUGINS_FOLDER), - listPreload: () => pluginPreloads, + listNative: () => pluginNatives, }, updater: { diff --git a/src/renderer/managers/plugins.ts b/src/renderer/managers/plugins.ts index def8ad43b..46ce9bb6e 100644 --- a/src/renderer/managers/plugins.ts +++ b/src/renderer/managers/plugins.ts @@ -16,10 +16,7 @@ interface PluginWrapper extends RepluggedPlugin { /** * @hidden */ -export const preload = RepluggedNative.plugins.listPreload() as Record< - string, - Record unknown> ->; +export const native = RepluggedNative.plugins.listNative(); export const plugins = new Map(); const running = new Set(); diff --git a/src/types/addon.ts b/src/types/addon.ts index 7a0227242..28feb14e8 100644 --- a/src/types/addon.ts +++ b/src/types/addon.ts @@ -62,7 +62,7 @@ export const plugin = common.extend({ type: z.literal("replugged-plugin"), renderer: z.string().optional(), plaintextPatches: z.string().optional(), - preload: z.string().optional(), + native: z.string().optional(), reloadRequired: z.boolean().optional(), }); diff --git a/src/types/index.ts b/src/types/index.ts index 03bffd73c..250012de5 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -22,7 +22,7 @@ export enum RepluggedIpcChannels { GET_THEME = "REPLUGGED_GET_THEME", UNINSTALL_THEME = "REPLUGGED_UNINSTALL_THEME", LIST_PLUGINS = "REPLUGGED_LIST_PLUGINS", - LIST_PLUGINS_PRELOAD = "REPLUGGED_LIST_PLUGINS_PRELOAD", + LIST_PLUGINS_NATIVE = "REPLUGGED_LIST_PLUGINS_NATIVE", GET_PLUGIN = "REPLUGGED_GET_PLUGIN", UNINSTALL_PLUGIN = "REPLUGGED_UNINSTALL_PLUGIN", REGISTER_RELOAD = "REPLUGGED_REGISTER_RELOAD", From 2e5ecad6eb76b05758cb5d76c9ba635ac79be969 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Sun, 28 Jul 2024 22:21:57 +0530 Subject: [PATCH 09/12] build errors --- bin/index.mts | 19 +++++++++++++++++++ src/main/ipc/plugins.ts | 10 +++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/bin/index.mts b/bin/index.mts index 81192aa99..84bbbc64e 100755 --- a/bin/index.mts +++ b/bin/index.mts @@ -332,6 +332,15 @@ async function buildPlugin({ watch, noInstall, production, noReload, addon }: Ar }; } + if (args.importer?.startsWith?.(path.resolve(manifest.native))) { + return { + errors: [ + { + text: `Unsupported import: ${args.path}\nOnly Native lib imports are supported in native files.`, + }, + ], + }; + } return { path: args.path, namespace: "replugged", @@ -353,6 +362,16 @@ async function buildPlugin({ watch, noInstall, production, noReload, addon }: Ar build.onResolve({ filter: /^react$/ }, (args) => { if (args.kind !== "import-statement") return undefined; + if (args.importer?.startsWith?.(path.resolve(manifest.native))) { + return { + errors: [ + { + text: `Unsupported import: ${args.path}\nOnly Native lib imports are supported in native files.`, + }, + ], + }; + } + return { path: "replugged/common/React", namespace: "replugged", diff --git a/src/main/ipc/plugins.ts b/src/main/ipc/plugins.ts index 62941a066..a7adff548 100644 --- a/src/main/ipc/plugins.ts +++ b/src/main/ipc/plugins.ts @@ -109,18 +109,14 @@ ipcMain.handle( // Ensure file changes are restricted to the base path throw new Error("Invalid plugin name"); } - const entries = Object.entries(require(nativePath)); + const entries = Object.entries<(...args: unknown[]) => unknown>(require(nativePath)); if (!entries.length) return acc; - const mappings = (acc[plugin.manifest.id] = {} as Record); + const mappings: Record = (acc[plugin.manifest.id] = {}); for (const [methodName, method] of entries) { const key = `RPPlugin-Native_${plugin.manifest.id}_${methodName}`; - ipcMain.handle( - key, - (_, ...args) => - (method as (...args: unknown[]) => unknown)(...args) /* For easy type when importing */, - ); + ipcMain.handle(key, (_, ...args) => method(...args) /* For easy type when importing */); mappings[methodName] = key; } return acc; From 19f81b06d9961d8557ba7a3de3b1325c5db30e48 Mon Sep 17 00:00:00 2001 From: yofukashino Date: Fri, 6 Dec 2024 09:31:37 +0530 Subject: [PATCH 10/12] preload types --- src/preload.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/preload.ts b/src/preload.ts index 2fa621ce6..92551c0b0 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -23,18 +23,19 @@ void ipcRenderer.invoke(RepluggedIpcChannels.GET_REPLUGGED_VERSION).then((v) => version = v; }); -const pluginNatives = {} as Record Promise>>; +const pluginNatives = {} as Record< + string, + Record Promise> +>; void ipcRenderer.invoke(RepluggedIpcChannels.LIST_PLUGINS_NATIVE).then((nativeList) => { for (const pluginId in nativeList) { const methods = nativeList[pluginId]; - const map = (pluginNatives[pluginId] = {} as Record< - string, - (...args: any[]) => Promise - >); + const map = {} as Record Promise>; for (const methodName in methods) { - map[methodName] = (...args: any[]) => ipcRenderer.invoke(methods[methodName], ...args); + map[methodName] = (...args: unknown[]) => ipcRenderer.invoke(methods[methodName], ...args); } + pluginNatives[pluginId] = map; } }); From 76b88b98538628dcb436dc3f9cdcb035641808bc Mon Sep 17 00:00:00 2001 From: yofukashino Date: Fri, 6 Dec 2024 10:07:55 +0530 Subject: [PATCH 11/12] more consistent with replugged --- bin/index.mts | 2 +- src/main/ipc/plugins.ts | 14 ++++++-------- src/preload.ts | 17 +++++++++-------- src/renderer/managers/plugins.ts | 10 +++++++++- src/types/addon.ts | 2 ++ src/types/index.ts | 8 +++++++- 6 files changed, 34 insertions(+), 19 deletions(-) diff --git a/bin/index.mts b/bin/index.mts index badcca41a..07ea01c5e 100755 --- a/bin/index.mts +++ b/bin/index.mts @@ -353,7 +353,7 @@ async function buildPlugin({ watch, noInstall, production, noReload, addon }: Ar if (args.kind !== "import-statement") return undefined; return { - path: `replugged/plugins/native["${manifest.id}"]`, + path: `replugged/plugins/pluginNatives.get("${manifest.id}")`, namespace: "replugged", }; }, diff --git a/src/main/ipc/plugins.ts b/src/main/ipc/plugins.ts index a7adff548..ac46593ce 100644 --- a/src/main/ipc/plugins.ts +++ b/src/main/ipc/plugins.ts @@ -9,13 +9,11 @@ import { extname, join, sep } from "path"; import { ipcMain, shell } from "electron"; import { RepluggedIpcChannels, type RepluggedPlugin } from "../../types"; import { plugin } from "../../types/addon"; -import type { Dirent, Stats } from "fs"; +import { type Dirent, type Stats } from "fs"; import { CONFIG_PATHS } from "src/util.mjs"; import { getSetting } from "./settings"; const PLUGINS_DIR = CONFIG_PATHS.plugins; -let PluginIpcMappings: Record>; - export const isFileAPlugin = (f: Dirent | Stats, name: string): boolean => { return f.isDirectory() || (f.isFile() && extname(name) === ".asar"); }; @@ -102,27 +100,27 @@ ipcMain.handle( const disabled = await getSetting("plugins", "disabled", []); const plugins = await listPlugins(); - PluginIpcMappings ??= plugins.reduce((acc: Record>, plugin) => { + return plugins.reduce((acc: Record>, plugin) => { if (!plugin.manifest.native || disabled.includes(plugin.manifest.id)) return acc; const nativePath = join(PLUGINS_DIR, plugin.path, plugin.manifest.native); if (!nativePath.startsWith(`${PLUGINS_DIR}${sep}`)) { // Ensure file changes are restricted to the base path throw new Error("Invalid plugin name"); } + const entries = Object.entries<(...args: unknown[]) => unknown>(require(nativePath)); if (!entries.length) return acc; - const mappings: Record = (acc[plugin.manifest.id] = {}); + const mappings: Record = {}; for (const [methodName, method] of entries) { - const key = `RPPlugin-Native_${plugin.manifest.id}_${methodName}`; + const key = `Replugged_Plugin_Native_[${plugin.manifest.id}]_${methodName}`; ipcMain.handle(key, (_, ...args) => method(...args) /* For easy type when importing */); mappings[methodName] = key; } + acc[plugin.manifest.id] = mappings; return acc; }, {}); - - return PluginIpcMappings; }, ); diff --git a/src/preload.ts b/src/preload.ts index f8091f130..5d1e52028 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -13,6 +13,7 @@ import type { InstallResultFailure, InstallResultSuccess, InstallerType, + PluginNativeMap, RepluggedPlugin, RepluggedTheme, } from "./types"; @@ -22,12 +23,10 @@ void ipcRenderer.invoke(RepluggedIpcChannels.GET_REPLUGGED_VERSION).then((v) => version = v; }); -const pluginNatives = {} as Record< - string, - Record Promise> ->; - -void ipcRenderer.invoke(RepluggedIpcChannels.LIST_PLUGINS_NATIVE).then((nativeList) => { +const mapNative = ( + nativeList: Record>, +): Array<[string, PluginNativeMap]> => { + const pluginNatives = {} as Record; for (const pluginId in nativeList) { const methods = nativeList[pluginId]; const map = {} as Record Promise>; @@ -36,7 +35,8 @@ void ipcRenderer.invoke(RepluggedIpcChannels.LIST_PLUGINS_NATIVE).then((nativeLi } pluginNatives[pluginId] = map; } -}); + return Object.entries(pluginNatives); +}; const RepluggedNative = { themes: { @@ -52,10 +52,11 @@ const RepluggedNative = { ipcRenderer.invoke(RepluggedIpcChannels.GET_PLUGIN, pluginPath), list: async (): Promise => ipcRenderer.invoke(RepluggedIpcChannels.LIST_PLUGINS), + listNative: async (): Promise> => + ipcRenderer.invoke(RepluggedIpcChannels.LIST_PLUGINS_NATIVE).then(mapNative), uninstall: async (pluginPath: string): Promise => ipcRenderer.invoke(RepluggedIpcChannels.UNINSTALL_PLUGIN, pluginPath), openFolder: () => ipcRenderer.send(RepluggedIpcChannels.OPEN_PLUGINS_FOLDER), - listNative: () => pluginNatives, }, updater: { diff --git a/src/renderer/managers/plugins.ts b/src/renderer/managers/plugins.ts index 5672f8a3f..88bb8a6a7 100644 --- a/src/renderer/managers/plugins.ts +++ b/src/renderer/managers/plugins.ts @@ -16,7 +16,7 @@ interface PluginWrapper extends RepluggedPlugin { /** * @hidden */ -export const native = RepluggedNative.plugins.listNative(); +export const pluginNatives = new Map unknown>>(); export const plugins = new Map(); const running = new Set(); @@ -49,6 +49,13 @@ function register(plugin: RepluggedPlugin): void { }); } +function registerNative([name, map]: [ + string, + Record unknown>, +]): void { + pluginNatives.set(name, map); +} + /** * Load all plugins * @@ -57,6 +64,7 @@ function register(plugin: RepluggedPlugin): void { */ export async function loadAll(): Promise { (await window.RepluggedNative.plugins.list()).forEach(register); + (await RepluggedNative.plugins.listNative()).forEach(registerNative); } /** diff --git a/src/types/addon.ts b/src/types/addon.ts index 28feb14e8..788f2e348 100644 --- a/src/types/addon.ts +++ b/src/types/addon.ts @@ -83,6 +83,8 @@ export interface PluginExports { [x: string]: unknown; } +export type PluginNativeMap = Record Promise>; + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type AddonSettings = { disabled?: string[]; diff --git a/src/types/index.ts b/src/types/index.ts index 2373e863b..f0ebfbc19 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -74,7 +74,13 @@ export interface RepluggedPlugin { hasCSS: boolean; } -export type { AnyAddonManifest, PluginExports, PluginManifest, ThemeManifest } from "./addon"; +export type { + AnyAddonManifest, + PluginExports, + PluginNativeMap, + PluginManifest, + ThemeManifest, +} from "./addon"; export * from "./coremods/commands"; export * from "./coremods/contextMenu"; export * from "./coremods/message"; From ae1caeea2362d9415534b7245b0616c967212f40 Mon Sep 17 00:00:00 2001 From: yofukashino Date: Fri, 6 Dec 2024 11:10:53 +0530 Subject: [PATCH 12/12] error handling --- src/main/ipc/plugins.ts | 36 +++++++++++++++++++++--------------- src/preload.ts | 5 ++++- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/main/ipc/plugins.ts b/src/main/ipc/plugins.ts index ac46593ce..f7aaa0f6e 100644 --- a/src/main/ipc/plugins.ts +++ b/src/main/ipc/plugins.ts @@ -101,25 +101,31 @@ ipcMain.handle( const plugins = await listPlugins(); return plugins.reduce((acc: Record>, plugin) => { - if (!plugin.manifest.native || disabled.includes(plugin.manifest.id)) return acc; - const nativePath = join(PLUGINS_DIR, plugin.path, plugin.manifest.native); - if (!nativePath.startsWith(`${PLUGINS_DIR}${sep}`)) { - // Ensure file changes are restricted to the base path - throw new Error("Invalid plugin name"); - } + try { + if (!plugin.manifest.native || disabled.includes(plugin.manifest.id)) return acc; + const nativePath = join(PLUGINS_DIR, plugin.path, plugin.manifest.native); + if (!nativePath.startsWith(`${PLUGINS_DIR}${sep}`)) { + // Ensure file changes are restricted to the base path + throw new Error("Invalid plugin name"); + } - const entries = Object.entries<(...args: unknown[]) => unknown>(require(nativePath)); - if (!entries.length) return acc; + const entries = Object.entries<(...args: unknown[]) => unknown>(require(nativePath)); + if (!entries.length) return acc; - const mappings: Record = {}; + const mappings: Record = {}; - for (const [methodName, method] of entries) { - const key = `Replugged_Plugin_Native_[${plugin.manifest.id}]_${methodName}`; - ipcMain.handle(key, (_, ...args) => method(...args) /* For easy type when importing */); - mappings[methodName] = key; + for (const [methodName, method] of entries) { + const key = `Replugged_Plugin_Native_[${plugin.manifest.id}]_${methodName}`; + ipcMain.handle(key, (_, ...args) => method(...args) /* For easy type when importing */); + mappings[methodName] = key; + } + acc[plugin.manifest.id] = mappings; + return acc; + } catch (e) { + console.error(`Error Loading Plugin Native`, plugin.manifest); + console.error(e); + return acc; } - acc[plugin.manifest.id] = mappings; - return acc; }, {}); }, ); diff --git a/src/preload.ts b/src/preload.ts index 5d1e52028..647d78146 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -31,7 +31,10 @@ const mapNative = ( const methods = nativeList[pluginId]; const map = {} as Record Promise>; for (const methodName in methods) { - map[methodName] = (...args: unknown[]) => ipcRenderer.invoke(methods[methodName], ...args); + map[methodName] = (...args: unknown[]) => + ipcRenderer.invoke(methods[methodName], ...args).catch((err) => { + throw new Error(err.message.split(": Error: ")[1]); + }); } pluginNatives[pluginId] = map; }