From 684ea70fb307d073d0517aeb3defad89a5ec08cb Mon Sep 17 00:00:00 2001 From: backryun Date: Sun, 16 Aug 2026 12:15:42 +0900 Subject: [PATCH] perf(electron): prune authoring docs from packages (#10359) --- scripts/build/electronRuntimeDocs.mjs | 65 +++++++++++++++++ scripts/build/prepare-electron-standalone.mjs | 9 +++ tests/unit/electron-packaging.test.ts | 70 ++++++++++++++++++- 3 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 scripts/build/electronRuntimeDocs.mjs diff --git a/scripts/build/electronRuntimeDocs.mjs b/scripts/build/electronRuntimeDocs.mjs new file mode 100644 index 0000000000..b9d5a8aa10 --- /dev/null +++ b/scripts/build/electronRuntimeDocs.mjs @@ -0,0 +1,65 @@ +import { existsSync, lstatSync, readdirSync, rmSync } from "node:fs"; +import { join, relative, resolve, sep } from "node:path"; + +export const ELECTRON_RUNTIME_DOC_PRUNE_RULES = Object.freeze({ + localeRootFiles: Object.freeze(["CHANGELOG.md"]), + authoringDirectories: Object.freeze(["docs/research", "docs/superpowers"]), +}); + +function payloadSize(targetPath) { + const stat = lstatSync(targetPath); + if (!stat.isDirectory()) { + return { files: 1, bytes: stat.size }; + } + + return readdirSync(targetPath).reduce( + (total, entry) => { + const payload = payloadSize(join(targetPath, entry)); + total.files += payload.files; + total.bytes += payload.bytes; + return total; + }, + { files: 0, bytes: 0 } + ); +} + +function removePayload(bundleRoot, relativePath, summary) { + const root = resolve(bundleRoot); + const targetPath = resolve(root, relativePath); + if (targetPath !== root && !targetPath.startsWith(`${root}${sep}`)) { + throw new Error(`[electron-docs] refusing to prune outside bundle root: ${relativePath}`); + } + if (!existsSync(targetPath)) return; + + const payload = payloadSize(targetPath); + rmSync(targetPath, { recursive: true, force: true }); + summary.removedFiles += payload.files; + summary.removedBytes += payload.bytes; + summary.removedPaths.push(relative(root, targetPath).split(sep).join("/")); +} + +/** + * Remove docs that are useful while authoring OmniRoute but are never read by + * the packaged desktop runtime. Canonical docs remain untouched; bundleRoot is + * the disposable Electron staging directory. + */ +export function pruneElectronRuntimeDocs(bundleRoot) { + const summary = { removedFiles: 0, removedBytes: 0, removedPaths: [] }; + const localesRoot = join(bundleRoot, "docs", "i18n"); + + if (existsSync(localesRoot)) { + for (const locale of readdirSync(localesRoot, { withFileTypes: true })) { + if (!locale.isDirectory()) continue; + for (const fileName of ELECTRON_RUNTIME_DOC_PRUNE_RULES.localeRootFiles) { + removePayload(bundleRoot, join("docs", "i18n", locale.name, fileName), summary); + } + } + } + + for (const relativePath of ELECTRON_RUNTIME_DOC_PRUNE_RULES.authoringDirectories) { + removePayload(bundleRoot, relativePath, summary); + } + + summary.removedPaths.sort(); + return summary; +} diff --git a/scripts/build/prepare-electron-standalone.mjs b/scripts/build/prepare-electron-standalone.mjs index e195f6480f..6265a94b31 100644 --- a/scripts/build/prepare-electron-standalone.mjs +++ b/scripts/build/prepare-electron-standalone.mjs @@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url"; import { spawnSync } from "node:child_process"; import { assembleStandalone } from "./assembleStandalone.mjs"; import { buildRebuildSpawnPlan } from "./electronRebuildPlan.mjs"; +import { pruneElectronRuntimeDocs } from "./electronRuntimeDocs.mjs"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -205,6 +206,14 @@ assembleStandalone({ materializeSymlinks: true, }); +const docsPrune = pruneElectronRuntimeDocs(ELECTRON_STANDALONE_DIR); +if (docsPrune.removedFiles > 0) { + console.log( + `[electron] pruned ${docsPrune.removedFiles} authoring doc file(s) ` + + `(${docsPrune.removedBytes} bytes) from the staging bundle` + ); +} + // Electron-UNIQUE post-assembly steps removeGeneratedElectronArtifacts(); diff --git a/tests/unit/electron-packaging.test.ts b/tests/unit/electron-packaging.test.ts index 49db9e30f7..1e231f9aca 100644 --- a/tests/unit/electron-packaging.test.ts +++ b/tests/unit/electron-packaging.test.ts @@ -1,7 +1,9 @@ import assert from "node:assert/strict"; -import { readFileSync } from "node:fs"; +import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; import { join } from "node:path"; import test from "node:test"; +import { pruneElectronRuntimeDocs } from "../../scripts/build/electronRuntimeDocs.mjs"; const ROOT = join(import.meta.dirname, "..", ".."); @@ -36,3 +38,69 @@ test("electron standalone assembly normalizes Turbopack hashed external imports" "Electron packages must strip Turbopack's hashed external package names before bundling" ); }); + +test("electron docs manifest prunes authoring payloads without removing runtime docs", () => { + const bundleRoot = mkdtempSync(join(tmpdir(), "omniroute-electron-docs-")); + const files = new Map([ + ["docs/openapi.yaml", "openapi: 3.1.0"], + ["docs/guides/CODEX-CLI-CONFIGURATION.md", "# Codex CLI"], + ["docs/i18n/ko/docs/guides/ELECTRON_GUIDE.md", "# Electron"], + ["docs/i18n/ko/CHANGELOG.md", "translated release history"], + ["docs/i18n/fr/CHANGELOG.md", "historique traduit"], + ["docs/research/desktop-notes.md", "authoring notes"], + ["docs/superpowers/plans/desktop-plan.md", "implementation plan"], + ]); + + try { + for (const [relativePath, content] of files) { + const absolutePath = join(bundleRoot, relativePath); + mkdirSync(join(absolutePath, ".."), { recursive: true }); + writeFileSync(absolutePath, content); + } + + const result = pruneElectronRuntimeDocs(bundleRoot); + + assert.deepEqual(result.removedPaths, [ + "docs/i18n/fr/CHANGELOG.md", + "docs/i18n/ko/CHANGELOG.md", + "docs/research", + "docs/superpowers", + ]); + assert.equal(result.removedFiles, 4); + assert.equal( + result.removedBytes, + Buffer.byteLength("translated release history") + + Buffer.byteLength("historique traduit") + + Buffer.byteLength("authoring notes") + + Buffer.byteLength("implementation plan") + ); + + assert.equal(existsSync(join(bundleRoot, "docs/openapi.yaml")), true); + assert.equal(existsSync(join(bundleRoot, "docs/guides/CODEX-CLI-CONFIGURATION.md")), true); + assert.equal(existsSync(join(bundleRoot, "docs/i18n/ko/docs/guides/ELECTRON_GUIDE.md")), true); + assert.equal(existsSync(join(bundleRoot, "docs/i18n/ko/CHANGELOG.md")), false); + assert.equal(existsSync(join(bundleRoot, "docs/research")), false); + assert.equal(existsSync(join(bundleRoot, "docs/superpowers")), false); + + assert.deepEqual(pruneElectronRuntimeDocs(bundleRoot), { + removedFiles: 0, + removedBytes: 0, + removedPaths: [], + }); + } finally { + rmSync(bundleRoot, { recursive: true, force: true }); + } +}); + +test("electron bundle preparation applies the runtime docs manifest to its staging tree", () => { + const prepareScript = readFileSync( + join(ROOT, "scripts", "build", "prepare-electron-standalone.mjs"), + "utf8" + ); + + assert.match( + prepareScript, + /pruneElectronRuntimeDocs\(ELECTRON_STANDALONE_DIR\)/, + "Electron staging must prune authoring docs before electron-builder copies the bundle" + ); +});