| 1 | import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; |
| 2 | import { tmpdir } from "node:os"; |
| 3 | import { join, resolve } from "node:path"; |
| 4 | import { createRequire } from "node:module"; |
| 5 | import { build } from "esbuild"; |
| 6 | import { _electron } from "playwright"; |
| 7 | |
| 8 | const root = resolve(import.meta.dirname, ".."); |
| 9 | |
| 10 | // The fixture uses production protocol, preload, diagnostic owner and Worker. |
| 11 | // Mode and paths remain JSON data, never interpolated executable source. |
| 12 | export async function performanceFixture(monitor = true, { archiveWorker = false, benchmark = false } = {}) { |
| 13 | const temp = mkdtempSync(join(tmpdir(), "reasonix-perf-")); |
| 14 | let app; |
| 15 | try { |
| 16 | mkdirSync(join(temp, "assets")); |
| 17 | writeFileSync(join(temp, "index.html"), '<!doctype html><title>Reasonix diagnostic fixture</title><button id="work">Work</button><pre id="output"></pre><script src="/assets/workload.js"></script>'); |
| 18 | await build({ |
| 19 | bundle: true, platform: "browser", format: "iife", target: "chrome130", |
| 20 | entryPoints: [join(import.meta.dirname, "fixtures/performance-renderer.ts")], |
| 21 | outfile: join(temp, "assets/workload.js"), |
| 22 | define: { __BUILD_COMMIT__: '"diagnostic-fixture"', __BUILD_CHANNEL__: '"dev"', "import.meta.env.DEV": "false", "import.meta.env.MODE": '"production"' }, |
| 23 | }); |
| 24 | const common = { bundle: true, platform: "node", format: "cjs", external: ["electron"] }; |
| 25 | await build({ ...common, entryPoints: [join(root, "src/preload/index.ts")], outfile: join(temp, "preload.cjs") }); |
| 26 | await build({ ...common, entryPoints: [join(root, "src/main/profileAnalysisWorker.ts")], outfile: join(temp, "profile-analysis.cjs") }); |
| 27 | let workerPath = join(temp, "profile-analysis.cjs"); |
| 28 | if (archiveWorker) { |
| 29 | const desktopRequire = createRequire(join(root, "../package.json")); |
| 30 | const packagerRequire = createRequire(desktopRequire.resolve("@electron/packager")); |
| 31 | const { createPackage } = packagerRequire("@electron/asar"); |
| 32 | const bundle = join(temp, "worker-bundle"); |
| 33 | mkdirSync(bundle); |
| 34 | await build({ ...common, entryPoints: [join(root, "src/main/profileAnalysisWorker.ts")], outfile: join(bundle, "profile-analysis.cjs") }); |
| 35 | await createPackage(bundle, join(temp, "worker.asar")); |
| 36 | workerPath = join(temp, "worker.asar/profile-analysis.cjs"); |
| 37 | } |
| 38 | writeFileSync(join(temp, "performance-config.json"), JSON.stringify({ monitor, benchmark, workerPath })); |
| 39 | await build({ ...common, entryPoints: [join(import.meta.dirname, "fixtures/performance-main.ts")], outfile: join(temp, "main.cjs") }); |
| 40 | app = await _electron.launch({ args: [join(temp, "main.cjs")] }); |
| 41 | const page = await app.firstWindow(); |
| 42 | await page.waitForFunction(() => Boolean(window.diagnosticFixture && window.reasonixDesktop)); |
| 43 | if (!benchmark) await page.bringToFront(); |
| 44 | return { app, page, temp, async close() { await app.close(); rmSync(temp, { recursive: true, force: true }); } }; |
| 45 | } catch (error) { |
| 46 | await app?.close(); |
| 47 | rmSync(temp, { recursive: true, force: true }); |
| 48 | throw error; |
| 49 | } |
| 50 | } |
| 51 |