Files
oh-my-pi/packages/coding-agent/test/startup-import-graph.test.ts
T
can1357 8427e69e1e fix(coding-agent): prevented puppeteer startup crashes
- Avoided value-imports of `puppeteer-core` in main startup files to prevent eager loading of the browser-data dependency graph.
- Used `import type` and literal string checks to defer `puppeteer-core` initialization until browser tools are actually invoked.
- Added a test to verify that `puppeteer-core` and `@puppeteer/browsers` remain off the eager startup import graph.
- Externalized additional heavy dependencies to reduce bundle size and improved minification settings in the build script.
2026-06-18 17:22:37 +02:00

55 lines
2.8 KiB
TypeScript

import { describe, expect, it } from "bun:test";
import * as path from "node:path";
const sourceRoot = path.join(import.meta.dir, "..", "src");
describe("startup import graph", () => {
it("keeps normal startup off the aggregate modes barrel", async () => {
const mainSource = await Bun.file(path.join(sourceRoot, "main.ts")).text();
expect(mainSource).toContain('import { InteractiveMode } from "./modes/interactive-mode";');
expect(mainSource).not.toContain('from "./modes"');
});
it("keeps branch-only mode runners out of the modes barrel", async () => {
const modesBarrelSource = await Bun.file(path.join(sourceRoot, "modes/index.ts")).text();
expect(modesBarrelSource).toContain('from "./interactive-mode"');
expect(modesBarrelSource).not.toContain("runAcpMode");
expect(modesBarrelSource).not.toContain("runPrintMode");
expect(modesBarrelSource).not.toContain("runRpcMode");
expect(modesBarrelSource).not.toContain("./rpc/rpc-mode");
});
it("keeps marketplace implementation behind the lightweight auto-update starter", async () => {
const mainSource = await Bun.file(path.join(sourceRoot, "main.ts")).text();
const starterSource = await Bun.file(
path.join(sourceRoot, "extensibility/plugins/marketplace-auto-update.ts"),
).text();
expect(mainSource).toContain('from "./extensibility/plugins/marketplace-auto-update"');
expect(mainSource).not.toContain('from "./extensibility/plugins/marketplace"');
expect(starterSource).toContain('await import("./marketplace")');
});
it("keeps puppeteer-core/@puppeteer/browsers off the eager startup graph", async () => {
// The builtin tool registry (tools/index.ts) statically imports BrowserTool, which
// reaches attach.ts -> registry.ts -> launch.ts -> tab-supervisor.ts. A *value*
// import from puppeteer-core (e.g. the TargetType enum) executes puppeteer-core's
// barrel at boot, which re-exports the node launchers + BrowserConnector and pulls in
// @puppeteer/browsers (the Chromium downloader). A packaging quirk in that subtree then
// becomes a hard startup crash (e.g. `Cannot find module './browser-data/browser-data.js'`)
// instead of a recoverable ToolError on first browser use. These modules must therefore
// import puppeteer packages as `import type` only. tab-worker.ts is excluded: it runs
// solely inside the spawned browser worker, off the main startup graph.
const browserDir = path.join(sourceRoot, "tools", "browser");
const eagerFiles = ["attach.ts", "registry.ts", "launch.ts", "tab-supervisor.ts"];
const valueImportRe = /^import\s+(?!type\b)[^;]*?from\s+["'](?:puppeteer-core|@puppeteer\/browsers)["']/gm;
for (const file of eagerFiles) {
const src = await Bun.file(path.join(browserDir, file)).text();
const offenders = src.match(valueImportRe) ?? [];
expect(offenders, `${file} value-imports puppeteer; use \`import type\``).toEqual([]);
}
});
});