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