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, }), )