1use std::{ffi::CString, ptr};
2
3use napi::{Env, Error, bindgen_prelude::*, sys::napi_value};
4use rspack_error::Error as RspackError;
5
6pub trait NapiErrorToRspackErrorExt {
7 fn to_rspack_error(self, env: &Env) -> RspackError;
8}
9
10impl NapiErrorToRspackErrorExt for Error {
11 fn to_rspack_error(self, env: &Env) -> RspackError {
12 let (reason, stack, backtrace, hide_stack) =
13 extract_stack_or_message_from_napi_error(env, self);
14 let mut err = RspackError::error(format!("{reason}\n{}", backtrace.unwrap_or_default()));
15 err.stack = stack;
16 err.hide_stack = hide_stack;
17 err
18 }
19}
20
21const fn get_backtrace() -> Option<String> {
22 None
23}
24
25#[inline(always)]
28fn extract_stack_or_message_from_napi_error(
29 env: &Env,
30 err: Error,
31) -> (String, Option<String>, Option<String>, Option<bool>) {
32 let maybe_reason = err.reason.clone();
33 match unsafe { ToNapiValue::to_napi_value(env.raw(), err) } {
34 Ok(napi_error) => {
35 let hide_stack = try_extract_string_value_from_property(env, napi_error, "hideStack")
36 .ok()
37 .map(|r| r == "true");
38 let message = match try_extract_string_value_from_property(env, napi_error, "message") {
39 Err(e) => format!("Unknown NAPI error {e}"),
40 Ok(message) => message,
41 };
42 let stack = try_extract_string_value_from_property(env, napi_error, "stack").ok();
43 (
44 if hide_stack.unwrap_or_default() {
45 message
46 } else {
47 stack.clone().unwrap_or(message)
48 },
49 stack,
50 get_backtrace(),
51 hide_stack,
52 )
53 }
54 Err(e) if maybe_reason.is_empty() => (
55 format!("Failed to extract NAPI error stack or message: {e}"),
56 None,
57 get_backtrace(),
58 None,
59 ),
60 Err(_) => (maybe_reason, None, None, None),
61 }
62}
63
64fn try_extract_string_value_from_property<S: AsRef<str>>(
65 env: &Env,
66 napi_object: napi_value,
67 property: S,
68) -> napi::Result<String> {
69 let property = CString::new(property.as_ref())?;
70
71 let mut value_ptr = ptr::null_mut();
72
73 check_status!(
74 unsafe {
75 sys::napi_get_named_property(env.raw(), napi_object, property.as_ptr(), &mut value_ptr)
76 },
77 "Failed to get the named property from object (property: {property:?})"
78 )?;
79
80 let mut str_len = 0;
81 check_status!(
82 unsafe {
83 sys::napi_get_value_string_utf8(env.raw(), value_ptr, ptr::null_mut(), 0, &mut str_len)
84 },
85 "Failed to get the length of the underlying property (property: {property:?})"
86 )?;
87
88 str_len += 1;
89 let mut buf = Vec::with_capacity(str_len);
90 let mut copied_len = 0;
91
92 check_status!(
93 unsafe {
94 sys::napi_get_value_string_utf8(
95 env.raw(),
96 value_ptr,
97 buf.as_mut_ptr(),
98 str_len,
99 &mut copied_len,
100 )
101 },
102 "Failed to get value of the property (property: {property:?})"
103 )?;
104
105 let mut buf = std::mem::ManuallyDrop::new(buf);
106
107 let buf = unsafe { Vec::from_raw_parts(buf.as_mut_ptr() as *mut u8, copied_len, copied_len) };
108
109 Ok(String::from_utf8_lossy(&buf).into_owned())
110}