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