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