this repo has no description
1import {
2 ExtensionManifest,
3 DetectedExtension,
4 ExtensionLoadSource,
5 constants
6} from "@moonlight-mod/types";
7import { readConfig } from "./config";
8import { getCoreExtensionsPath, getExtensionsPath } from "./util/data";
9import Logger from "./util/logger";
10
11const logger = new Logger("core/extension");
12
13async function findManifests(dir: string): Promise<string[]> {
14 const ret = [];
15
16 if (await moonlightFS.exists(dir)) {
17 for (const file of await moonlightFS.readdir(dir)) {
18 const path = moonlightFS.join(dir, file);
19 if (file === "manifest.json") {
20 ret.push(path);
21 }
22
23 if (!(await moonlightFS.isFile(path))) {
24 ret.push(...(await findManifests(path)));
25 }
26 }
27 }
28
29 return ret;
30}
31
32async function loadDetectedExtensions(
33 dir: string,
34 type: ExtensionLoadSource
35): Promise<DetectedExtension[]> {
36 const ret: DetectedExtension[] = [];
37
38 const manifests = await findManifests(dir);
39 for (const manifestPath of manifests) {
40 try {
41 if (!(await moonlightFS.exists(manifestPath))) continue;
42 const dir = moonlightFS.dirname(manifestPath);
43
44 const manifest: ExtensionManifest = JSON.parse(
45 await moonlightFS.readFileString(manifestPath)
46 );
47
48 const webPath = moonlightFS.join(dir, "index.js");
49 const nodePath = moonlightFS.join(dir, "node.js");
50 const hostPath = moonlightFS.join(dir, "host.js");
51
52 // if none exist (empty manifest) don't give a shit
53 if (
54 !moonlightFS.exists(webPath) &&
55 !moonlightFS.exists(nodePath) &&
56 !moonlightFS.exists(hostPath)
57 ) {
58 continue;
59 }
60
61 const web = (await moonlightFS.exists(webPath))
62 ? await moonlightFS.readFileString(webPath)
63 : undefined;
64
65 let url: string | undefined = undefined;
66 const urlPath = moonlightFS.join(dir, constants.repoUrlFile);
67 if (
68 type === ExtensionLoadSource.Normal &&
69 (await moonlightFS.exists(urlPath))
70 ) {
71 url = await moonlightFS.readFileString(urlPath);
72 }
73
74 const wpModules: Record<string, string> = {};
75 const wpModulesPath = moonlightFS.join(dir, "webpackModules");
76 if (await moonlightFS.exists(wpModulesPath)) {
77 const wpModulesFile = await moonlightFS.readdir(wpModulesPath);
78
79 for (const wpModuleFile of wpModulesFile) {
80 if (wpModuleFile.endsWith(".js")) {
81 wpModules[wpModuleFile.replace(".js", "")] =
82 await moonlightFS.readFileString(
83 moonlightFS.join(wpModulesPath, wpModuleFile)
84 );
85 }
86 }
87 }
88
89 ret.push({
90 id: manifest.id,
91 manifest,
92 source: {
93 type,
94 url
95 },
96 scripts: {
97 web,
98 webPath: web != null ? webPath : undefined,
99 webpackModules: wpModules,
100 nodePath: (await moonlightFS.exists(nodePath)) ? nodePath : undefined,
101 hostPath: (await moonlightFS.exists(hostPath)) ? hostPath : undefined
102 }
103 });
104 } catch (e) {
105 logger.error(e, "Failed to load extension");
106 }
107 }
108
109 return ret;
110}
111
112async function getExtensionsNative(): Promise<DetectedExtension[]> {
113 const config = await readConfig();
114 const res = [];
115
116 res.push(
117 ...(await loadDetectedExtensions(
118 getCoreExtensionsPath(),
119 ExtensionLoadSource.Core
120 ))
121 );
122
123 res.push(
124 ...(await loadDetectedExtensions(
125 await getExtensionsPath(),
126 ExtensionLoadSource.Normal
127 ))
128 );
129
130 for (const devSearchPath of config.devSearchPaths ?? []) {
131 res.push(
132 ...(await loadDetectedExtensions(
133 devSearchPath,
134 ExtensionLoadSource.Developer
135 ))
136 );
137 }
138
139 return res;
140}
141
142async function getExtensionsBrowser(): Promise<DetectedExtension[]> {
143 const ret: DetectedExtension[] = [];
144
145 const coreExtensionsFs: Record<string, string> = JSON.parse(
146 // @ts-expect-error shut up
147 _moonlight_coreExtensionsStr
148 );
149 const coreExtensions = Array.from(
150 new Set(Object.keys(coreExtensionsFs).map((x) => x.split("/")[0]))
151 );
152
153 for (const ext of coreExtensions) {
154 if (!coreExtensionsFs[`${ext}/index.js`]) continue;
155 const manifest = JSON.parse(coreExtensionsFs[`${ext}/manifest.json`]);
156 const web = coreExtensionsFs[`${ext}/index.js`];
157
158 const wpModules: Record<string, string> = {};
159 const wpModulesPath = `${ext}/webpackModules`;
160 for (const wpModuleFile of Object.keys(coreExtensionsFs)) {
161 if (wpModuleFile.startsWith(wpModulesPath)) {
162 wpModules[
163 wpModuleFile.replace(wpModulesPath + "/", "").replace(".js", "")
164 ] = coreExtensionsFs[wpModuleFile];
165 }
166 }
167
168 ret.push({
169 id: manifest.id,
170 manifest,
171 source: {
172 type: ExtensionLoadSource.Core
173 },
174 scripts: {
175 web,
176 webpackModules: wpModules
177 }
178 });
179 }
180
181 if (await moonlightFS.exists("/extensions")) {
182 ret.push(
183 ...(await loadDetectedExtensions(
184 "/extensions",
185 ExtensionLoadSource.Normal
186 ))
187 );
188 }
189
190 return ret;
191}
192
193export async function getExtensions(): Promise<DetectedExtension[]> {
194 webPreload: {
195 return moonlightNode.extensions;
196 }
197
198 browser: {
199 return await getExtensionsBrowser();
200 }
201
202 nodeTarget: {
203 return await getExtensionsNative();
204 }
205
206 throw new Error("Called getExtensions() outside of node-preload/web-preload");
207}