this repo has no description
1import { webFrame, ipcRenderer, contextBridge } from "electron";
2import fs from "node:fs";
3import path from "node:path";
4
5import { readConfig, writeConfig } from "@moonlight-mod/core/config";
6import { constants, MoonlightBranch } from "@moonlight-mod/types";
7import { getExtensions } from "@moonlight-mod/core/extension";
8import { getExtensionsPath, getMoonlightDir } from "@moonlight-mod/core/util/data";
9import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
10import { loadExtensions, loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
11import createFS from "@moonlight-mod/core/fs";
12import { registerCors, registerBlocked, getDynamicCors } from "@moonlight-mod/core/cors";
13
14let initialized = false;
15
16function setCors() {
17 const data = getDynamicCors();
18 ipcRenderer.invoke(constants.ipcSetCorsList, data.cors);
19 ipcRenderer.invoke(constants.ipcSetBlockedList, data.blocked);
20}
21
22async function injectGlobals() {
23 global.moonlightNodeSandboxed = {
24 fs: createFS(),
25 addCors(url) {
26 registerCors(url);
27 if (initialized) setCors();
28 },
29 addBlocked(url) {
30 registerBlocked(url);
31 if (initialized) setCors();
32 }
33 };
34
35 const config = await readConfig();
36 initLogger(config);
37 const extensions = await getExtensions();
38 const processedExtensions = await loadExtensions(extensions);
39 const moonlightDir = await getMoonlightDir();
40 const extensionsPath = await getExtensionsPath();
41
42 function getConfig(ext: string) {
43 const val = config.extensions[ext];
44 if (val == null || typeof val === "boolean") return undefined;
45 return val.config;
46 }
47
48 global.moonlightNode = {
49 config,
50 extensions,
51 processedExtensions,
52 nativesCache: {},
53 isBrowser: false,
54
55 version: MOONLIGHT_VERSION,
56 branch: MOONLIGHT_BRANCH as MoonlightBranch,
57
58 getConfig,
59 getConfigOption: <T>(ext: string, name: string) => {
60 const config = getConfig(ext);
61 if (config == null) return undefined;
62 const option = config[name];
63 if (option == null) return undefined;
64 return option as T;
65 },
66 getNatives: (ext: string) => global.moonlightNode.nativesCache[ext],
67 getLogger: (id: string) => {
68 return new Logger(id);
69 },
70
71 getMoonlightDir() {
72 return moonlightDir;
73 },
74 getExtensionDir: (ext: string) => {
75 return path.join(extensionsPath, ext);
76 },
77 writeConfig
78 };
79
80 await loadProcessedExtensions(processedExtensions);
81 contextBridge.exposeInMainWorld("moonlightNode", moonlightNode);
82
83 const extCors = moonlightNode.processedExtensions.extensions.flatMap((x) => x.manifest.cors ?? []);
84 for (const cors of extCors) {
85 registerCors(cors);
86 }
87
88 for (const repo of moonlightNode.config.repositories) {
89 const url = new URL(repo);
90 url.pathname = "/";
91 registerCors(url.toString());
92 }
93
94 const extBlocked = moonlightNode.processedExtensions.extensions.flatMap((e) => e.manifest.blocked ?? []);
95 for (const blocked of extBlocked) {
96 registerBlocked(blocked);
97 }
98
99 setCors();
100
101 initialized = true;
102}
103
104async function loadPreload() {
105 const webPreloadPath = path.join(__dirname, "web-preload.js");
106 const webPreload = fs.readFileSync(webPreloadPath, "utf8");
107 await webFrame.executeJavaScript(webPreload);
108}
109
110async function init(oldPreloadPath: string) {
111 try {
112 await injectGlobals();
113 await loadPreload();
114 } catch (e) {
115 const message = e instanceof Error ? e.stack : e;
116 await ipcRenderer.invoke(constants.ipcMessageBox, {
117 title: "moonlight node-preload error",
118 message: message
119 });
120 }
121
122 // Let Discord start even if we fail
123 if (oldPreloadPath) require(oldPreloadPath);
124}
125
126const oldPreloadPath: string = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
127init(oldPreloadPath);