this repo has no description
at v1.0.8 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 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.opendirSync(wpModulesDir); 139 for await (const wpModule of wpModules) { 140 if (wpModule.isFile()) { 141 entryPoints.push( 142 `packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}` 143 ); 144 } else { 145 for (const fileExt of ["ts", "tsx"]) { 146 const path = `packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}/index.${fileExt}`; 147 if (fs.existsSync(path)) { 148 entryPoints.push({ 149 in: path, 150 out: `webpackModules/${wpModule.name}` 151 }); 152 } 153 } 154 } 155 } 156 } 157 158 const wpImportPlugin = { 159 name: "webpackImports", 160 setup(build) { 161 build.onResolve({ filter: /^@moonlight-mod\/wp\// }, (args) => { 162 const wpModule = args.path.replace(/^@moonlight-mod\/wp\//, ""); 163 return { 164 path: wpModule, 165 external: true 166 }; 167 }); 168 } 169 }; 170 171 const esbuildConfig = { 172 entryPoints, 173 outdir, 174 175 format: "cjs", 176 platform: "node", 177 178 treeShaking: true, 179 bundle: true, 180 sourcemap: prod ? false : "inline", 181 182 external, 183 184 logOverride: { 185 "commonjs-variable-in-esm": "verbose" 186 }, 187 logLevel: "silent", 188 plugins: [ 189 ...(copyManifest 190 ? [ 191 copyStaticFiles({ 192 src: `./packages/core-extensions/src/${ext}/manifest.json`, 193 dest: `./dist/core-extensions/${ext}/manifest.json` 194 }) 195 ] 196 : []), 197 wpImportPlugin, 198 deduplicatedLogging, 199 taggedBuildLog(`ext/${ext}`) 200 ] 201 }; 202 203 if (watch) { 204 const ctx = await esbuild.context(esbuildConfig); 205 await ctx.watch(); 206 } else { 207 await esbuild.build(esbuildConfig); 208 } 209} 210 211const promises = []; 212 213for (const [name, entry] of Object.entries(config)) { 214 promises.push(build(name, entry)); 215} 216 217const coreExtensions = fs.readdirSync("./packages/core-extensions/src"); 218for (const ext of coreExtensions) { 219 let copiedManifest = false; 220 221 for (const fileExt of ["ts", "tsx"]) { 222 for (const type of ["index", "node", "host"]) { 223 if ( 224 fs.existsSync( 225 `./packages/core-extensions/src/${ext}/${type}.${fileExt}` 226 ) 227 ) { 228 promises.push(buildExt(ext, type, !copiedManifest, fileExt)); 229 copiedManifest = true; 230 } 231 } 232 } 233} 234 235await Promise.all(promises);