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 {