this repo has no description

Merge pull request #153 from moonlight-mod/notnite/entrypoint-cleanup

Entrypoint cleanup

Changed files
+80 -111
packages
core-extensions
src
disableSentry
injector
src
node-preload
src
types
web-preload
src
+13 -15
packages/core-extensions/src/disableSentry/node.ts
···
const logger = moonlightNode.getLogger("disableSentry");
-
if (!ipcRenderer.sendSync(constants.ipcGetIsMoonlightDesktop)) {
-
const preloadPath = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
-
try {
-
const sentryPath = require.resolve(resolve(preloadPath, "..", "node_modules", "@sentry", "electron"));
-
require.cache[sentryPath] = new Module(sentryPath, require.cache[require.resolve(preloadPath)]);
-
require.cache[sentryPath]!.exports = {
-
init: () => {},
-
setTag: () => {},
-
setUser: () => {},
-
captureMessage: () => {}
-
};
-
logger.debug("Stubbed Sentry node side!");
-
} catch (err) {
-
logger.error("Failed to stub Sentry:", err);
-
}
}
···
const logger = moonlightNode.getLogger("disableSentry");
+
const preloadPath = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
+
try {
+
const sentryPath = require.resolve(resolve(preloadPath, "..", "node_modules", "@sentry", "electron"));
+
require.cache[sentryPath] = new Module(sentryPath, require.cache[require.resolve(preloadPath)]);
+
require.cache[sentryPath]!.exports = {
+
init: () => {},
+
setTag: () => {},
+
setUser: () => {},
+
captureMessage: () => {}
+
};
+
logger.debug("Stubbed Sentry node side!");
+
} catch (err) {
+
logger.error("Failed to stub Sentry:", err);
}
+47 -84
packages/injector/src/index.ts
···
} from "electron";
import Module from "node:module";
import { constants, MoonlightBranch } from "@moonlight-mod/types";
-
import { readConfig } from "@moonlight-mod/core/config";
import { getExtensions } from "@moonlight-mod/core/extension";
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
import { loadExtensions, loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
import EventEmitter from "node:events";
-
import { join, resolve } from "node:path";
import persist from "@moonlight-mod/core/persist";
import createFS from "@moonlight-mod/core/fs";
const logger = new Logger("injector");
let oldPreloadPath: string | undefined;
let corsAllow: string[] = [];
let blockedUrls: RegExp[] = [];
-
let isMoonlightDesktop = false;
-
let hasOpenAsar = false;
-
let openAsarConfigPreload: string | undefined;
const scriptUrls = ["web.", "sentry."];
const blockedScripts = new Set<string>();
···
ipcMain.on(constants.ipcGetAppData, (e) => {
e.returnValue = app.getPath("appData");
});
-
ipcMain.on(constants.ipcGetIsMoonlightDesktop, (e) => {
-
e.returnValue = isMoonlightDesktop;
});
ipcMain.handle(constants.ipcMessageBox, (_, opts) => {
electron.dialog.showMessageBoxSync(opts);
···
headers[csp] = [stringified];
}
-
function removeOpenAsarEventIfPresent(eventHandler: (...args: any[]) => void) {
-
const code = eventHandler.toString();
-
if (code.indexOf("bw.webContents.on('dom-ready'") > -1) {
-
electron.app.off("browser-window-created", eventHandler);
-
}
-
}
-
class BrowserWindow extends ElectronBrowserWindow {
constructor(opts: BrowserWindowConstructorOptions) {
const isMainWindow = opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1;
···
// this is needed because multiple webRequest handlers cannot be registered at once
cb({ cancel: blockedUrls.some((u) => u.test(details.url)) });
});
-
-
if (hasOpenAsar) {
-
// Remove DOM injections
-
// Settings can still be opened via:
-
// `DiscordNative.ipc.send("DISCORD_UPDATED_QUOTES","o")`
-
// @ts-expect-error Electron internals
-
const events = electron.app._events["browser-window-created"];
-
if (Array.isArray(events)) {
-
for (const event of events) {
-
removeOpenAsarEventIfPresent(event);
-
}
-
} else if (events != null) {
-
removeOpenAsarEventIfPresent(events);
-
}
-
-
// Config screen fails to context bridge properly
-
// Less than ideal, but better than disabling it everywhere
-
if (opts.webPreferences!.preload === openAsarConfigPreload) {
-
opts.webPreferences!.sandbox = false;
-
}
-
}
}
}
···
writable: false
});
-
export async function inject(asarPath: string) {
-
isMoonlightDesktop = asarPath === "moonlightDesktop";
global.moonlightNodeSandboxed = {
fs: createFS(),
// These aren't supposed to be used from host
-
addCors(url) {},
-
addBlocked(url) {}
};
try {
-
const config = await readConfig();
initLogger(config);
const extensions = await getExtensions();
// Duplicated in node-preload... oops
function getConfig(ext: string) {
···
}
global.moonlightHost = {
asarPath,
-
config,
events: new EventEmitter(),
-
extensions,
-
processedExtensions: {
-
extensions: [],
-
dependencyGraph: new Map()
-
},
version: MOONLIGHT_VERSION,
branch: MOONLIGHT_BRANCH as MoonlightBranch,
getConfig,
-
getConfigOption: <T>(ext: string, name: string) => {
-
const config = getConfig(ext);
-
if (config == null) return undefined;
-
const option = config[name];
-
if (option == null) return undefined;
-
return option as T;
},
-
getLogger: (id: string) => {
return new Logger(id);
}
};
-
// Check if we're running with OpenAsar
-
try {
-
require.resolve(join(asarPath, "updater", "updater.js"));
-
hasOpenAsar = true;
-
openAsarConfigPreload = resolve(asarPath, "config", "preload.js");
-
// eslint-disable-next-line no-empty
-
} catch {}
-
-
if (hasOpenAsar) {
-
// Disable command line switch injection
-
// I personally think that the command line switches should be vetted by
-
// the user and not just "trust that these are sane defaults that work
-
// always". I'm not hating on Ducko or anything, I'm just opinionated.
-
// Someone can always make a command line modifier plugin, thats the point
-
// of having host modules.
-
try {
-
const cmdSwitchesPath = require.resolve(join(asarPath, "cmdSwitches.js"));
-
require.cache[cmdSwitchesPath] = new Module(cmdSwitchesPath, require.cache[require.resolve(asarPath)]);
-
require.cache[cmdSwitchesPath]!.exports = () => {};
-
} catch (error) {
-
logger.error("Failed to disable OpenAsar's command line flags:", error);
-
}
-
}
-
patchElectron();
-
global.moonlightHost.processedExtensions = await loadExtensions(extensions);
await loadProcessedExtensions(global.moonlightHost.processedExtensions);
} catch (error) {
logger.error("Failed to inject:", error);
}
-
if (isMoonlightDesktop) return;
-
-
if (!hasOpenAsar && !isMoonlightDesktop) {
persist(asarPath);
}
-
// Need to do this instead of require() or it breaks require.main
-
// @ts-expect-error Module internals
-
Module._load(asarPath, Module, true);
}
function patchElectron() {
···
} from "electron";
import Module from "node:module";
import { constants, MoonlightBranch } from "@moonlight-mod/types";
+
import { readConfig, writeConfig } from "@moonlight-mod/core/config";
import { getExtensions } from "@moonlight-mod/core/extension";
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
import { loadExtensions, loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
import EventEmitter from "node:events";
+
import path from "node:path";
import persist from "@moonlight-mod/core/persist";
import createFS from "@moonlight-mod/core/fs";
+
import { getConfigOption, getManifest, setConfigOption } from "@moonlight-mod/core/util/config";
+
import { getExtensionsPath, getMoonlightDir } from "@moonlight-mod/core/util/data";
const logger = new Logger("injector");
let oldPreloadPath: string | undefined;
let corsAllow: string[] = [];
let blockedUrls: RegExp[] = [];
+
let injectorConfig: InjectorConfig | undefined;
const scriptUrls = ["web.", "sentry."];
const blockedScripts = new Set<string>();
···
ipcMain.on(constants.ipcGetAppData, (e) => {
e.returnValue = app.getPath("appData");
});
+
ipcMain.on(constants.ipcGetInjectorConfig, (e) => {
+
e.returnValue = injectorConfig;
});
ipcMain.handle(constants.ipcMessageBox, (_, opts) => {
electron.dialog.showMessageBoxSync(opts);
···
headers[csp] = [stringified];
}
class BrowserWindow extends ElectronBrowserWindow {
constructor(opts: BrowserWindowConstructorOptions) {
const isMainWindow = opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1;
···
// this is needed because multiple webRequest handlers cannot be registered at once
cb({ cancel: blockedUrls.some((u) => u.test(details.url)) });
});
}
}
···
writable: false
});
+
type InjectorConfig = { disablePersist?: boolean; disableLoad?: boolean };
+
export async function inject(asarPath: string, _injectorConfig?: InjectorConfig) {
+
injectorConfig = _injectorConfig;
+
global.moonlightNodeSandboxed = {
fs: createFS(),
// These aren't supposed to be used from host
+
addCors() {},
+
addBlocked() {}
};
try {
+
let config = await readConfig();
initLogger(config);
const extensions = await getExtensions();
+
const processedExtensions = await loadExtensions(extensions);
+
const moonlightDir = await getMoonlightDir();
+
const extensionsPath = await getExtensionsPath();
// Duplicated in node-preload... oops
function getConfig(ext: string) {
···
}
global.moonlightHost = {
+
get config() {
+
return config;
+
},
+
extensions,
+
processedExtensions,
asarPath,
events: new EventEmitter(),
version: MOONLIGHT_VERSION,
branch: MOONLIGHT_BRANCH as MoonlightBranch,
getConfig,
+
getConfigOption(ext, name) {
+
const manifest = getManifest(extensions, ext);
+
return getConfigOption(ext, name, config, manifest?.settings);
+
},
+
setConfigOption(ext, name, value) {
+
setConfigOption(config, ext, name, value);
+
this.writeConfig(config);
+
},
+
async writeConfig(newConfig) {
+
await writeConfig(newConfig);
+
config = newConfig;
},
+
+
getLogger(id) {
return new Logger(id);
+
},
+
getMoonlightDir() {
+
return moonlightDir;
+
},
+
getExtensionDir: (ext: string) => {
+
return path.join(extensionsPath, ext);
}
};
patchElectron();
await loadProcessedExtensions(global.moonlightHost.processedExtensions);
} catch (error) {
logger.error("Failed to inject:", error);
}
+
if (injectorConfig?.disablePersist !== true) {
persist(asarPath);
}
+
if (injectorConfig?.disableLoad !== true) {
+
// Need to do this instead of require() or it breaks require.main
+
// @ts-expect-error Module internals
+
Module._load(asarPath, Module, true);
+
}
}
function patchElectron() {
+4 -5
packages/node-preload/src/index.ts
···
setConfigOption(config, ext, name, value);
this.writeConfig(config);
},
getNatives: (ext: string) => global.moonlightNode.nativesCache[ext],
getLogger: (id: string) => {
return new Logger(id);
},
-
getMoonlightDir() {
return moonlightDir;
},
getExtensionDir: (ext: string) => {
return path.join(extensionsPath, ext);
-
},
-
async writeConfig(newConfig) {
-
await writeConfig(newConfig);
-
config = newConfig;
}
};
···
setConfigOption(config, ext, name, value);
this.writeConfig(config);
},
+
async writeConfig(newConfig) {
+
await writeConfig(newConfig);
+
config = newConfig;
+
},
getNatives: (ext: string) => global.moonlightNode.nativesCache[ext],
getLogger: (id: string) => {
return new Logger(id);
},
getMoonlightDir() {
return moonlightDir;
},
getExtensionDir: (ext: string) => {
return path.join(extensionsPath, ext);
}
};
+1 -1
packages/types/src/constants.ts
···
export const ipcGetOldPreloadPath = "_moonlight_getOldPreloadPath";
export const ipcGetAppData = "_moonlight_getAppData";
-
export const ipcGetIsMoonlightDesktop = "_moonlight_getIsMoonlightDesktop";
export const ipcMessageBox = "_moonlight_messageBox";
export const ipcSetCorsList = "_moonlight_setCorsList";
export const ipcSetBlockedList = "_moonlight_setBlockedList";
···
export const ipcGetOldPreloadPath = "_moonlight_getOldPreloadPath";
export const ipcGetAppData = "_moonlight_getAppData";
+
export const ipcGetInjectorConfig = "_moonlight_getInjectorConfig";
export const ipcMessageBox = "_moonlight_messageBox";
export const ipcSetCorsList = "_moonlight_setCorsList";
export const ipcSetBlockedList = "_moonlight_setBlockedList";
+11 -5
packages/types/src/globals.ts
···
import { MoonlightFS } from "./fs";
export type MoonlightHost = {
-
asarPath: string;
config: Config;
-
events: EventEmitter;
extensions: DetectedExtension[];
processedExtensions: ProcessedExtensions;
version: string;
branch: MoonlightBranch;
getConfig: (ext: string) => ConfigExtension["config"];
getConfigOption: <T>(ext: string, name: string) => T | undefined;
getLogger: (id: string) => Logger;
};
export type MoonlightNode = {
···
getConfig: (ext: string) => ConfigExtension["config"];
getConfigOption: <T>(ext: string, name: string) => T | undefined;
setConfigOption: <T>(ext: string, name: string, value: T) => void;
getNatives: (ext: string) => any | undefined;
getLogger: (id: string) => Logger;
-
getMoonlightDir: () => string;
getExtensionDir: (ext: string) => string;
-
writeConfig: (config: Config) => Promise<void>;
};
export type MoonlightNodeSandboxed = {
···
unpatched: Set<IdentifiedPatch>;
pendingModules: Set<IdentifiedWebpackModule>;
enabledExtensions: Set<string>;
-
apiLevel: number;
events: MoonlightEventEmitter<EventType, EventPayloads>;
patchingInternals: {
onModuleLoad: (moduleId: string | string[], callback: (moduleId: string) => void) => void;
···
version: string;
branch: MoonlightBranch;
// Re-exports for ease of use
getConfig: MoonlightNode["getConfig"];
getConfigOption: MoonlightNode["getConfigOption"];
setConfigOption: MoonlightNode["setConfigOption"];
getNatives: (ext: string) => any | undefined;
getLogger: (id: string) => Logger;
lunast: LunAST;
moonmap: Moonmap;
};
···
import { MoonlightFS } from "./fs";
export type MoonlightHost = {
config: Config;
extensions: DetectedExtension[];
processedExtensions: ProcessedExtensions;
+
asarPath: string;
+
events: EventEmitter;
version: string;
branch: MoonlightBranch;
getConfig: (ext: string) => ConfigExtension["config"];
getConfigOption: <T>(ext: string, name: string) => T | undefined;
+
setConfigOption: <T>(ext: string, name: string, value: T) => void;
+
writeConfig: (config: Config) => Promise<void>;
+
getLogger: (id: string) => Logger;
+
getMoonlightDir: () => string;
+
getExtensionDir: (ext: string) => string;
};
export type MoonlightNode = {
···
getConfig: (ext: string) => ConfigExtension["config"];
getConfigOption: <T>(ext: string, name: string) => T | undefined;
setConfigOption: <T>(ext: string, name: string, value: T) => void;
+
writeConfig: (config: Config) => Promise<void>;
getNatives: (ext: string) => any | undefined;
getLogger: (id: string) => Logger;
getMoonlightDir: () => string;
getExtensionDir: (ext: string) => string;
};
export type MoonlightNodeSandboxed = {
···
unpatched: Set<IdentifiedPatch>;
pendingModules: Set<IdentifiedWebpackModule>;
enabledExtensions: Set<string>;
events: MoonlightEventEmitter<EventType, EventPayloads>;
patchingInternals: {
onModuleLoad: (moduleId: string | string[], callback: (moduleId: string) => void) => void;
···
version: string;
branch: MoonlightBranch;
+
apiLevel: number;
// Re-exports for ease of use
getConfig: MoonlightNode["getConfig"];
getConfigOption: MoonlightNode["getConfigOption"];
setConfigOption: MoonlightNode["setConfigOption"];
+
writeConfig: MoonlightNode["writeConfig"];
getNatives: (ext: string) => any | undefined;
getLogger: (id: string) => Logger;
+
lunast: LunAST;
moonmap: Moonmap;
};
+4 -1
packages/web-preload/src/index.ts
···
const logger = new Logger("web-preload");
window.moonlight = {
-
apiLevel: constants.apiLevel,
unpatched: new Set(),
pendingModules: new Set(),
enabledExtensions: new Set(),
events: createEventEmitter<EventType, EventPayloads>(),
patchingInternals: {
onModuleLoad,
···
version: MOONLIGHT_VERSION,
branch: MOONLIGHT_BRANCH as MoonlightBranch,
getConfig: moonlightNode.getConfig.bind(moonlightNode),
getConfigOption: moonlightNode.getConfigOption.bind(moonlightNode),
setConfigOption: moonlightNode.setConfigOption.bind(moonlightNode),
getNatives: moonlightNode.getNatives.bind(moonlightNode),
getLogger(id) {
return new Logger(id);
},
lunast: new LunAST(),
moonmap: new Moonmap()
};
···
const logger = new Logger("web-preload");
window.moonlight = {
unpatched: new Set(),
pendingModules: new Set(),
enabledExtensions: new Set(),
+
events: createEventEmitter<EventType, EventPayloads>(),
patchingInternals: {
onModuleLoad,
···
version: MOONLIGHT_VERSION,
branch: MOONLIGHT_BRANCH as MoonlightBranch,
+
apiLevel: constants.apiLevel,
getConfig: moonlightNode.getConfig.bind(moonlightNode),
getConfigOption: moonlightNode.getConfigOption.bind(moonlightNode),
setConfigOption: moonlightNode.setConfigOption.bind(moonlightNode),
+
writeConfig: moonlightNode.writeConfig.bind(moonlightNode),
getNatives: moonlightNode.getNatives.bind(moonlightNode),
getLogger(id) {
return new Logger(id);
},
+
lunast: new LunAST(),
moonmap: new Moonmap()
};