From 5ac585a938b7ca69c7ab983a918ff2719aa87174 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Mon, 6 Apr 2026 08:22:22 +0000 Subject: [PATCH] fix(cli): override rolldown panic hook with vite-plus branding (#1287) Since rolldown_binding is bundled into the same NAPI binary, its module_init panic hook catches all panics and misleadingly shows "Rolldown panicked" with a link to rolldown's issue tracker. Replace it at the start of run() with a vite-plus specific hook that correctly attributes panics and links to the vite-plus bug report. Closes #1285 --- packages/cli/binding/src/lib.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/cli/binding/src/lib.rs b/packages/cli/binding/src/lib.rs index 4da1358464..58bea6471d 100644 --- a/packages/cli/binding/src/lib.rs +++ b/packages/cli/binding/src/lib.rs @@ -123,6 +123,27 @@ fn format_error_message(error: &(dyn StdError + 'static)) -> String { message } +/// Install a Vite+ panic hook so panics are correctly attributed to Vite+. +/// +/// Discards any previously set hook (e.g. rolldown's) via double `take_hook`: +/// first call removes the current hook, second captures the restored default. +/// Safe to call regardless of whether a custom hook was installed. +#[allow(clippy::disallowed_macros)] +fn setup_panic_hook() { + static ONCE: std::sync::Once = std::sync::Once::new(); + ONCE.call_once(|| { + let _ = std::panic::take_hook(); + let default_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |info| { + eprintln!("Vite+ panicked. This is a bug in Vite+, not your code."); + default_hook(info); + eprintln!( + "\nPlease report this issue at: https://github.com/voidzero-dev/vite-plus/issues/new?template=bug_report.yml" + ); + })); + }); +} + /// Main entry point for the CLI, called from JavaScript. /// /// This is an async function that spawns a new thread for the non-Send async code @@ -130,6 +151,7 @@ fn format_error_message(error: &(dyn StdError + 'static)) -> String { /// and process JavaScript callbacks (via ThreadsafeFunction). #[napi] pub async fn run(options: CliOptions) -> Result { + setup_panic_hook(); // Use provided cwd or current directory let mut cwd = current_dir()?; if let Some(options_cwd) = options.cwd {