From 1032d499ec2007510ea4b19d845425a66b8c591d Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Thu, 16 Jul 2026 01:35:07 +0000 Subject: [PATCH] fix(core): restore integration cwd detection and harden build exit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "unify builtin devtools plugin" refactor dropped `cwd` when calling `DevToolsBuiltin()` from `DevTools()`, so optional-integration detection silently fell back to `process.cwd()` again — regressing #423 and breaking the `resolves optional integrations from the configured project directory` unit test on every OS in CI. Also hardens two unrelated hang risks found while chasing the failing e2e job: the CLI's `build` command now exits explicitly once the static build finishes instead of relying on the event loop draining naturally, and the Iconify SVG fetch now times out instead of hanging forever on a stalled connection. The e2e harness's CLI-build spawn gets an explicit timeout too, so a future hang fails fast with a clear message instead of silently burning the whole test timeout. Fixed with the help of an agent. --- e2e/src/harness.ts | 5 +++++ packages/core/src/client/webcomponents/utils/iconify.ts | 5 ++++- packages/core/src/node/cli.ts | 8 +++++++- packages/core/src/node/plugins/index.ts | 1 + 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/e2e/src/harness.ts b/e2e/src/harness.ts index 535f7d316..75be65a6c 100644 --- a/e2e/src/harness.ts +++ b/e2e/src/harness.ts @@ -25,6 +25,11 @@ export async function buildPluginFixture(fixtureDir: string): Promise { export async function buildCliFixture(fixtureDir: string, outDir = '.vite-devtools'): Promise { const result = await x('node', [cliBin, 'build', '--root', fixtureDir, '--outDir', outDir], { throwOnError: true, + // The CLI process should exit as soon as the build finishes. Without a + // bound here, a hang in the child process (e.g. an open handle that + // prevents the event loop from draining) silently burns the whole test + // timeout with no indication of where it got stuck. + timeout: 60_000, }) if (result.exitCode !== 0) throw new Error(`vite-devtools build failed:\n${result.stderr}`) diff --git a/packages/core/src/client/webcomponents/utils/iconify.ts b/packages/core/src/client/webcomponents/utils/iconify.ts index 29bd116d5..958366588 100644 --- a/packages/core/src/client/webcomponents/utils/iconify.ts +++ b/packages/core/src/client/webcomponents/utils/iconify.ts @@ -24,7 +24,10 @@ export async function getIconifySvg(collection: string, icon: string) { async function _get() { const url = `https://api.iconify.design/${collection}/${icon}.svg?color=currentColor&width=100%` - const svg = await fetch(url).then(res => res.text()) + // Bound the request so a stalled connection (offline / flaky CDN / firewall + // black-holing the host) rejects instead of hanging forever — the caller + // already degrades a rejected fetch to a blank icon. + const svg = await fetch(url, { signal: AbortSignal.timeout(10_000) }).then(res => res.text()) return purify.sanitize(svg) } } diff --git a/packages/core/src/node/cli.ts b/packages/core/src/node/cli.ts index c311619cd..7d05afbfc 100644 --- a/packages/core/src/node/cli.ts +++ b/packages/core/src/node/cli.ts @@ -17,7 +17,13 @@ cli // Action .action(async (options) => { const { build } = await import('./cli-commands') - return await build(options) + await build(options) + // A static build has no long-lived work left once it returns. Exiting + // explicitly makes termination independent of whatever a `devtools.setup()` + // hook (ours or a third-party plugin's) may have left open — a stray timer, + // socket, or watcher would otherwise hang the process indefinitely instead + // of a build that should just finish and exit. + process.exit(0) }) cli diff --git a/packages/core/src/node/plugins/index.ts b/packages/core/src/node/plugins/index.ts index b250239e5..dd167aaca 100644 --- a/packages/core/src/node/plugins/index.ts +++ b/packages/core/src/node/plugins/index.ts @@ -49,6 +49,7 @@ export async function DevTools(options: DevToolsOptions = {}): Promise plugins.unshift( ...await DevToolsBuiltin({ + cwd: options.cwd, vitePlusLaunchers: builtinDevTools, }), )