this repo has no description
at v1.2.0 8.4 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"); 16const browser = process.argv.includes("--browser"); 17const mv2 = process.argv.includes("--mv2"); 18 19const buildBranch = process.env.MOONLIGHT_BRANCH ?? "dev"; 20const buildVersion = process.env.MOONLIGHT_VERSION ?? "dev"; 21 22const external = [ 23 "electron", 24 "fs", 25 "path", 26 "module", 27 "discord", // mappings 28 29 // Silence an esbuild warning 30 "./node-preload.js" 31]; 32 33let lastMessages = new Set(); 34/** @type {import("esbuild").Plugin} */ 35const deduplicatedLogging = { 36 name: "deduplicated-logging", 37 setup(build) { 38 build.onStart(() => { 39 lastMessages.clear(); 40 }); 41 42 build.onEnd(async (result) => { 43 const formatted = await Promise.all([ 44 esbuild.formatMessages(result.warnings, { 45 kind: "warning", 46 color: true 47 }), 48 esbuild.formatMessages(result.errors, { kind: "error", color: true }) 49 ]).then((a) => a.flat()); 50 51 // console.log(formatted); 52 for (const message of formatted) { 53 if (lastMessages.has(message)) continue; 54 lastMessages.add(message); 55 console.log(message.trim()); 56 } 57 }); 58 } 59}; 60 61const timeFormatter = new Intl.DateTimeFormat(undefined, { 62 hour: "numeric", 63 minute: "numeric", 64 second: "numeric", 65 hour12: false 66}); 67/** @type {import("esbuild").Plugin} */ 68const taggedBuildLog = (tag) => ({ 69 name: "build-log", 70 setup(build) { 71 build.onEnd((result) => { 72 console.log( 73 `[${timeFormatter.format(new Date())}] [${tag}] build finished` 74 ); 75 }); 76 } 77}); 78 79async function build(name, entry) { 80 let outfile = path.join("./dist", name + ".js"); 81 const browserDir = mv2 ? "browser-mv2" : "browser"; 82 if (name === "browser") outfile = path.join("./dist", browserDir, "index.js"); 83 84 const dropLabels = []; 85 const labels = { 86 injector: ["injector"], 87 nodePreload: ["node-preload"], 88 webPreload: ["web-preload"], 89 browser: ["browser"], 90 91 webTarget: ["web-preload", "browser"], 92 nodeTarget: ["node-preload", "injector"] 93 }; 94 for (const [label, targets] of Object.entries(labels)) { 95 if (!targets.includes(name)) { 96 dropLabels.push(label); 97 } 98 } 99 100 const define = { 101 MOONLIGHT_ENV: `"${name}"`, 102 MOONLIGHT_PROD: prod.toString(), 103 MOONLIGHT_BRANCH: `"${buildBranch}"`, 104 MOONLIGHT_VERSION: `"${buildVersion}"` 105 }; 106 107 for (const iterName of [ 108 "injector", 109 "node-preload", 110 "web-preload", 111 "browser" 112 ]) { 113 const snake = iterName.replace(/-/g, "_").toUpperCase(); 114 define[`MOONLIGHT_${snake}`] = (name === iterName).toString(); 115 } 116 117 const nodeDependencies = ["glob"]; 118 const ignoredExternal = name === "web-preload" ? nodeDependencies : []; 119 120 const plugins = [deduplicatedLogging, taggedBuildLog(name)]; 121 if (name === "browser") { 122 plugins.push( 123 copyStaticFiles({ 124 src: mv2 125 ? "./packages/browser/manifestv2.json" 126 : "./packages/browser/manifest.json", 127 dest: `./dist/${browserDir}/manifest.json` 128 }) 129 ); 130 131 if (!mv2) { 132 plugins.push( 133 copyStaticFiles({ 134 src: "./packages/browser/modifyResponseHeaders.json", 135 dest: `./dist/${browserDir}/modifyResponseHeaders.json` 136 }) 137 ); 138 plugins.push( 139 copyStaticFiles({ 140 src: "./packages/browser/blockLoading.json", 141 dest: `./dist/${browserDir}/blockLoading.json` 142 }) 143 ); 144 } 145 146 plugins.push( 147 copyStaticFiles({ 148 src: mv2 149 ? "./packages/browser/src/background-mv2.js" 150 : "./packages/browser/src/background.js", 151 dest: `./dist/${browserDir}/background.js` 152 }) 153 ); 154 } 155 156 /** @type {import("esbuild").BuildOptions} */ 157 const esbuildConfig = { 158 entryPoints: [entry], 159 outfile, 160 161 format: "iife", 162 globalName: "module.exports", 163 164 platform: ["web-preload", "browser"].includes(name) ? "browser" : "node", 165 166 treeShaking: true, 167 bundle: true, 168 minify: prod, 169 sourcemap: "inline", 170 171 external: [...ignoredExternal, ...external], 172 173 define, 174 dropLabels, 175 176 logLevel: "silent", 177 plugins 178 }; 179 180 if (name === "browser") { 181 const coreExtensionsJson = {}; 182 183 // eslint-disable-next-line no-inner-declarations 184 function readDir(dir) { 185 const files = fs.readdirSync(dir); 186 for (const file of files) { 187 const filePath = dir + "/" + file; 188 const normalizedPath = filePath.replace("./dist/core-extensions/", ""); 189 if (fs.statSync(filePath).isDirectory()) { 190 readDir(filePath); 191 } else { 192 coreExtensionsJson[normalizedPath] = fs.readFileSync( 193 filePath, 194 "utf8" 195 ); 196 } 197 } 198 } 199 200 readDir("./dist/core-extensions"); 201 202 esbuildConfig.banner = { 203 js: `window._moonlight_coreExtensionsStr = ${JSON.stringify( 204 JSON.stringify(coreExtensionsJson) 205 )};` 206 }; 207 } 208 209 if (watch) { 210 const ctx = await esbuild.context(esbuildConfig); 211 await ctx.watch(); 212 } else { 213 await esbuild.build(esbuildConfig); 214 } 215} 216 217async function buildExt(ext, side, copyManifest, fileExt) { 218 const outdir = path.join("./dist", "core-extensions", ext); 219 if (!fs.existsSync(outdir)) { 220 fs.mkdirSync(outdir, { recursive: true }); 221 } 222 223 const entryPoints = [ 224 `packages/core-extensions/src/${ext}/${side}.${fileExt}` 225 ]; 226 227 const wpModulesDir = `packages/core-extensions/src/${ext}/webpackModules`; 228 if (fs.existsSync(wpModulesDir) && side === "index") { 229 const wpModules = fs.opendirSync(wpModulesDir); 230 for await (const wpModule of wpModules) { 231 if (wpModule.isFile()) { 232 entryPoints.push( 233 `packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}` 234 ); 235 } else { 236 for (const fileExt of ["ts", "tsx"]) { 237 const path = `packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}/index.${fileExt}`; 238 if (fs.existsSync(path)) { 239 entryPoints.push({ 240 in: path, 241 out: `webpackModules/${wpModule.name}` 242 }); 243 } 244 } 245 } 246 } 247 } 248 249 const wpImportPlugin = { 250 name: "webpackImports", 251 setup(build) { 252 build.onResolve({ filter: /^@moonlight-mod\/wp\// }, (args) => { 253 const wpModule = args.path.replace(/^@moonlight-mod\/wp\//, ""); 254 return { 255 path: wpModule, 256 external: true 257 }; 258 }); 259 } 260 }; 261 262 const esbuildConfig = { 263 entryPoints, 264 outdir, 265 266 format: "iife", 267 globalName: "module.exports", 268 platform: "node", 269 270 treeShaking: true, 271 bundle: true, 272 sourcemap: prod ? false : "inline", 273 274 external, 275 276 logOverride: { 277 "commonjs-variable-in-esm": "verbose" 278 }, 279 logLevel: "silent", 280 plugins: [ 281 ...(copyManifest 282 ? [ 283 copyStaticFiles({ 284 src: `./packages/core-extensions/src/${ext}/manifest.json`, 285 dest: `./dist/core-extensions/${ext}/manifest.json` 286 }) 287 ] 288 : []), 289 wpImportPlugin, 290 deduplicatedLogging, 291 taggedBuildLog(`ext/${ext}`) 292 ] 293 }; 294 295 if (watch) { 296 const ctx = await esbuild.context(esbuildConfig); 297 await ctx.watch(); 298 } else { 299 await esbuild.build(esbuildConfig); 300 } 301} 302 303const promises = []; 304 305if (browser) { 306 build("browser", "packages/browser/src/index.ts"); 307} else { 308 for (const [name, entry] of Object.entries(config)) { 309 promises.push(build(name, entry)); 310 } 311 312 const coreExtensions = fs.readdirSync("./packages/core-extensions/src"); 313 for (const ext of coreExtensions) { 314 let copiedManifest = false; 315 316 for (const fileExt of ["ts", "tsx"]) { 317 for (const type of ["index", "node", "host"]) { 318 if ( 319 fs.existsSync( 320 `./packages/core-extensions/src/${ext}/${type}.${fileExt}` 321 ) 322 ) { 323 promises.push(buildExt(ext, type, !copiedManifest, fileExt)); 324 copiedManifest = true; 325 } 326 } 327 } 328 } 329} 330 331await Promise.all(promises);