this repo has no description
1/* eslint-disable no-console */
2import * as esbuild from "esbuild";
3import copyStaticFiles from "esbuild-copy-static-files";
4
5import path from "path";
6import fs from "fs";
7
8const config = {
9 injector: "packages/injector/src/index.ts",
10 "node-preload": "packages/node-preload/src/index.ts",
11 "web-preload": "packages/web-preload/src/index.ts"
12};
13
14const prod = process.env.NODE_ENV === "production";
15const watch = process.argv.includes("--watch");
16
17const external = [
18 "electron",
19 "fs",
20 "path",
21 "module",
22 "events",
23 "original-fs", // wtf asar?
24
25 // Silence an esbuild warning
26 "./node-preload.js"
27];
28
29let lastMessages = new Set();
30/** @type {import("esbuild").Plugin} */
31const deduplicatedLogging = {
32 name: "deduplicated-logging",
33 setup(build) {
34 build.onStart(() => {
35 lastMessages.clear();
36 });
37
38 build.onEnd(async (result) => {
39 const formatted = await Promise.all([
40 esbuild.formatMessages(result.warnings, {
41 kind: "warning",
42 color: true
43 }),
44 esbuild.formatMessages(result.errors, { kind: "error", color: true })
45 ]).then((a) => a.flat());
46
47 // console.log(formatted);
48 for (const message of formatted) {
49 if (lastMessages.has(message)) continue;
50 lastMessages.add(message);
51 console.log(message.trim());
52 }
53 });
54 }
55};
56
57const timeFormatter = new Intl.DateTimeFormat(undefined, {
58 hour: "numeric",
59 minute: "numeric",
60 second: "numeric",
61 hour12: false
62});
63/** @type {import("esbuild").Plugin} */
64const taggedBuildLog = (tag) => ({
65 name: "build-log",
66 setup(build) {
67 build.onEnd((result) => {
68 console.log(
69 `[${timeFormatter.format(new Date())}] [${tag}] build finished`
70 );
71 });
72 }
73});
74
75async function build(name, entry) {
76 const outfile = path.join("./dist", name + ".js");
77
78 const dropLabels = [];
79 if (name !== "injector") dropLabels.push("injector");
80 if (name !== "node-preload") dropLabels.push("nodePreload");
81 if (name !== "web-preload") dropLabels.push("webPreload");
82
83 const define = {
84 MOONLIGHT_ENV: `"${name}"`,
85 MOONLIGHT_PROD: prod.toString()
86 };
87
88 for (const iterName of Object.keys(config)) {
89 const snake = iterName.replace(/-/g, "_").toUpperCase();
90 define[`MOONLIGHT_${snake}`] = (name === iterName).toString();
91 }
92
93 const nodeDependencies = ["glob"];
94 const ignoredExternal = name === "web-preload" ? nodeDependencies : [];
95
96 /** @type {import("esbuild").BuildOptions} */
97 const esbuildConfig = {
98 entryPoints: [entry],
99 outfile,
100
101 format: "cjs",
102 platform: name === "web-preload" ? "browser" : "node",
103
104 treeShaking: true,
105 bundle: true,
106 minify: prod,
107 sourcemap: "inline",
108
109 external: [...ignoredExternal, ...external],
110
111 define,
112 dropLabels,
113
114 logLevel: "silent",
115 plugins: [deduplicatedLogging, taggedBuildLog(name)]
116 };
117
118 if (watch) {
119 const ctx = await esbuild.context(esbuildConfig);
120 await ctx.watch();
121 } else {
122 await esbuild.build(esbuildConfig);
123 }
124}
125
126async function buildExt(ext, side, copyManifest, fileExt) {
127 const outdir = path.join("./dist", "core-extensions", ext);
128 if (!fs.existsSync(outdir)) {
129 fs.mkdirSync(outdir, { recursive: true });
130 }
131
132 const entryPoints = [
133 `packages/core-extensions/src/${ext}/${side}.${fileExt}`
134 ];
135
136 const wpModulesDir = `packages/core-extensions/src/${ext}/webpackModules`;
137 if (fs.existsSync(wpModulesDir) && side === "index") {
138 const wpModules = fs.readdirSync(wpModulesDir);
139 for (const wpModule of wpModules) {
140 entryPoints.push(
141 `packages/core-extensions/src/${ext}/webpackModules/${wpModule}`
142 );
143 }
144 }
145
146 const wpImportPlugin = {
147 name: "webpackImports",
148 setup(build) {
149 build.onResolve({ filter: /^@moonlight-mod\/wp\// }, (args) => {
150 const wpModule = args.path.replace(/^@moonlight-mod\/wp\//, "");
151 return {
152 path: wpModule,
153 external: true
154 };
155 });
156 }
157 };
158
159 const esbuildConfig = {
160 entryPoints,
161 outdir,
162
163 format: "cjs",
164 platform: "node",
165
166 treeShaking: true,
167 bundle: true,
168 sourcemap: prod ? false : "inline",
169
170 external,
171
172 logOverride: {
173 "commonjs-variable-in-esm": "verbose"
174 },
175 logLevel: "silent",
176 plugins: [
177 ...(copyManifest
178 ? [
179 copyStaticFiles({
180 src: `./packages/core-extensions/src/${ext}/manifest.json`,
181 dest: `./dist/core-extensions/${ext}/manifest.json`
182 })
183 ]
184 : []),
185 wpImportPlugin,
186 deduplicatedLogging,
187 taggedBuildLog(`ext/${ext}`)
188 ]
189 };
190
191 if (watch) {
192 const ctx = await esbuild.context(esbuildConfig);
193 await ctx.watch();
194 } else {
195 await esbuild.build(esbuildConfig);
196 }
197}
198
199const promises = [];
200
201for (const [name, entry] of Object.entries(config)) {
202 promises.push(build(name, entry));
203}
204
205const coreExtensions = fs.readdirSync("./packages/core-extensions/src");
206for (const ext of coreExtensions) {
207 let copiedManifest = false;
208
209 for (const fileExt of ["ts", "tsx"]) {
210 for (const type of ["index", "node", "host"]) {
211 if (
212 fs.existsSync(
213 `./packages/core-extensions/src/${ext}/${type}.${fileExt}`
214 )
215 ) {
216 promises.push(buildExt(ext, type, !copiedManifest, fileExt));
217 copiedManifest = true;
218 }
219 }
220 }
221}
222
223await Promise.all(promises);