perf(cli): defer Playwright and isomorphic-git until a command needs them

Load browser composition after CLI scenario validation and load Git support only for local reference trees with Git metadata. Preserve default outputs and actual command behavior.

Refs #1065.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
This commit is contained in:
Kaoru0822 authored and GitHub committed 2026-10-10 13:16:44 +08:00
1 parent bb6fa1c6f5
commit eeb83f6581
3 files changed
+81 -3

No files matched your search

+2 -2
View File
@@ -2,8 +2,6 @@ import fs from "node:fs";
import { lstat, readFile } from "node:fs/promises";
import { join, resolve } from "node:path";
import { resolveRef } from "isomorphic-git";
export type ReferenceSourceVcsInfo =
| {
readonly kind: "git";
@@ -42,6 +40,8 @@ export const readReferenceSourceVcs = async (
};
const resolveSourceHead = async (root: string): Promise<string> => {
// isomorphic-git is loaded on first use so CLI and MCP startup skip it.
const { resolveRef } = await import("isomorphic-git");
const dotgit = join(root, ".git");
if (!(await lstat(dotgit)).isFile())
return resolveRef({ fs, dir: root, ref: "HEAD" });
+3 -1
View File
@@ -2,7 +2,6 @@ import { Cli } from "incur";
import { z } from "zod";
import { captureBrowserScenario } from "./application/BrowserScenarioCaptureService.js";
import { createBrowserScenarioProvider } from "./composition/browserScenario.js";
import { CLI_COMMANDS } from "./cliCommandNames.js";
import { parseCliJsonInput, resolveCliJsonPaths } from "./cliJsonInput.js";
import { logCliCommand } from "./cliLogging.js";
@@ -44,6 +43,9 @@ export const registerBrowserScenarioCommands = (
projectInputIssues(scenario.error.issues, input.value),
),
);
// Load Playwright only for this command, not during CLI startup.
const { createBrowserScenarioProvider } =
await import("./composition/browserScenario.js");
const result = await captureBrowserScenario(
createBrowserScenarioProvider(environment),
scenario.data,
@@ -0,0 +1,76 @@
import { execFile } from "node:child_process";
import { mkdir } from "node:fs/promises";
import { join } from "node:path";
import { promisify } from "node:util";
import { describe, expect, it } from "vitest";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
const execute = promisify(execFile);
const MARKER = "REA_TEST_RESOLVED ";
// Dependencies that only one command uses; startup must not resolve them.
const DEFERRED_PACKAGES = ["playwright-core", "isomorphic-git"] as const;
// Report each resolved deferred package on stderr without changing resolution.
const RESOLUTION_HOOK = `data:text/javascript,${encodeURIComponent(`
import { registerHooks } from "node:module";
const packages = ${JSON.stringify(DEFERRED_PACKAGES)};
registerHooks({
resolve(specifier, context, next) {
const resolved = next(specifier, context);
const name = packages.find((item) =>
resolved.url.includes("/node_modules/" + item + "/"),
);
if (name !== undefined)
process.stderr.write(${JSON.stringify(MARKER)} + name + "\\n");
return resolved;
},
});
`)}`;
const resolvedPackages = async (
arguments_: readonly string[],
): Promise<ReadonlySet<string>> => {
const { stderr } = await execute(
process.execPath,
[`--import=${RESOLUTION_HOOK}`, ...arguments_],
{ cwd: process.cwd(), maxBuffer: 16 * 1_024 * 1_024 },
);
return new Set(
stderr
.split("\n")
.filter((line) => line.startsWith(MARKER))
.map((line) => line.slice(MARKER.length)),
);
};
const evaluate = (source: string): readonly string[] => [
"--input-type=module",
"--eval",
source,
];
describe("CLI startup imports", () => {
it("detects each deferred package when its command code loads", async () => {
await expect(
resolvedPackages(
evaluate('await import("./dist/composition/browserScenario.js");'),
),
).resolves.toContain("playwright-core");
const source = await createTestTempDirectory("rea-startup-imports-");
await mkdir(join(source, ".git"));
await expect(
resolvedPackages(
evaluate(
`const { readReferenceSourceVcs } = await import("./dist/application/ReferenceSourceVcsAdapter.js"); await readReferenceSourceVcs(${JSON.stringify(source)});`,
),
),
).resolves.toContain("isomorphic-git");
});
it("does not load command-specific packages to register commands", async () => {
await expect(
resolvedPackages(["scripts/rea.mjs", "--help"]),
).resolves.toEqual(new Set());
});
});