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