1use std::rc::Rc;
2
3use indexmap::IndexMap;
4use log::debug;
5use napi::{JsNumber, JsObject, JsString, JsValue, Unknown, ValueType};
6use stylex_ast::ast::{
7 convertors::{create_bool_expr, create_null_expr, create_number_expr, create_string_expr},
8 factories::{create_array_expression, create_key_value_prop, create_object_expression},
9};
10use stylex_structures::stylex_env::{EnvEntry, JSFunction};
11use stylex_utils::swc::get_default_expr_ctx;
12use swc_core::ecma::{
13 ast::{Expr, ExprOrSpread, Lit, PropName, PropOrSpread},
14 utils::ExprExt,
15};
16
17thread_local! {
18 static NAPI_ENV_RAW: std::cell::Cell<Option<napi::sys::napi_env>> =
19 const { std::cell::Cell::new(None) };
20}
21
22pub(crate) fn with_napi_env<F, R>(env: &napi::Env, f: F) -> R
24where
25 F: FnOnce() -> R,
26{
27 NAPI_ENV_RAW.set(Some(env.raw()));
28 let result = f();
29 NAPI_ENV_RAW.set(None);
30 result
31}
32
33pub(crate) fn parse_env_object(
35 env: &napi::Env,
36 obj: &JsObject,
37) -> napi::Result<IndexMap<String, EnvEntry>> {
38 let names = obj.get_property_names()?;
39 let len = names.get_array_length()?;
40 let mut map = IndexMap::new();
41
42 for i in 0..len {
43 let key: JsString = names.get_element(i)?;
44 let key_str = key.into_utf8()?.as_str()?.to_string();
45 let value: Unknown = obj.get_named_property(&key_str)?;
46 let env_entry = parse_env_value(env, value)?;
47 map.insert(key_str, env_entry);
48 }
49
50 Ok(map)
51}
52
53fn parse_env_value(env: &napi::Env, value: Unknown) -> napi::Result<EnvEntry> {
54 match value.get_type()? {
55 ValueType::String => {
56 let s: JsString = unsafe { value.cast()? };
57 Ok(EnvEntry::Expr(create_string_expr(s.into_utf8()?.as_str()?)))
58 },
59 ValueType::Number => {
60 let n: JsNumber = unsafe { value.cast()? };
61 Ok(EnvEntry::Expr(create_number_expr(n.get_double()?)))
62 },
63 ValueType::Boolean => {
64 let raw_env = env.raw();
65 let mut b = false;
66 let status = unsafe { napi::sys::napi_get_value_bool(raw_env, value.raw(), &mut b) };
67 if status != napi::sys::Status::napi_ok {
68 return Err(napi::Error::from_reason("Failed to get boolean value"));
69 }
70 Ok(EnvEntry::Expr(create_bool_expr(b)))
71 },
72 ValueType::Null | ValueType::Undefined => Ok(EnvEntry::Expr(create_null_expr())),
73 ValueType::Function => parse_env_function(env, value.raw()),
74 ValueType::Object => Ok(EnvEntry::Expr(napi_value_to_expr(env.raw(), value.raw()))),
75 _ => Ok(EnvEntry::Expr(create_null_expr())),
76 }
77}
78
79fn parse_env_function(env: &napi::Env, js_fn_raw: napi::sys::napi_value) -> napi::Result<EnvEntry> {
80 let raw_env = env.raw();
81
82 let mut ref_ptr: napi::sys::napi_ref = std::ptr::null_mut();
83 let status = unsafe { napi::sys::napi_create_reference(raw_env, js_fn_raw, 1, &mut ref_ptr) };
84
85 if status != napi::sys::Status::napi_ok {
86 return Err(napi::Error::from_reason(
87 "Failed to create reference for env function",
88 ));
89 }
90
91 let ref_ptr = Rc::new(EnvFnRef {
92 ref_ptr,
93 env: raw_env,
94 });
95
96 Ok(EnvEntry::Function(JSFunction::new(
97 move |args: Vec<Expr>| {
98 let raw_env = NAPI_ENV_RAW
99 .get()
100 .expect("NAPI env not available during env function call");
101
102 let mut js_fn_raw: napi::sys::napi_value = std::ptr::null_mut();
103 unsafe {
104 napi::sys::napi_get_reference_value(raw_env, ref_ptr.ref_ptr, &mut js_fn_raw);
105 }
106
107 let js_args: Vec<napi::sys::napi_value> = args
108 .iter()
109 .map(|arg| expr_to_napi_value(raw_env, arg))
110 .collect();
111
112 let mut result: napi::sys::napi_value = std::ptr::null_mut();
113 let mut undefined: napi::sys::napi_value = std::ptr::null_mut();
114 unsafe {
115 napi::sys::napi_get_undefined(raw_env, &mut undefined);
116 napi::sys::napi_call_function(
117 raw_env,
118 undefined,
119 js_fn_raw,
120 js_args.len(),
121 js_args.as_ptr(),
122 &mut result,
123 );
124 }
125
126 napi_value_to_expr(raw_env, result)
127 },
128 )))
129}
130
131fn expr_to_napi_value(raw_env: napi::sys::napi_env, expr: &Expr) -> napi::sys::napi_value {
132 let mut result: napi::sys::napi_value = std::ptr::null_mut();
133 match expr {
134 Expr::Lit(Lit::Str(s)) => {
135 let val = s.value.as_str().unwrap_or("");
136 unsafe {
137 napi::sys::napi_create_string_utf8(
138 raw_env,
139 val.as_ptr() as *const _,
140 val.len() as isize,
141 &mut result,
142 );
143 }
144 },
145 Expr::Lit(Lit::Num(n)) => unsafe {
146 napi::sys::napi_create_double(raw_env, n.value, &mut result);
147 },
148 Expr::Lit(Lit::Bool(b)) => unsafe {
149 napi::sys::napi_get_boolean(raw_env, b.value, &mut result);
150 },
151 Expr::Object(obj) => unsafe {
152 napi::sys::napi_create_object(raw_env, &mut result);
153 for prop in &obj.props {
154 if let PropOrSpread::Prop(prop) = prop
155 && let Some(kv) = prop.as_key_value()
156 {
157 let key = match &kv.key {
158 PropName::Ident(id) => id.sym.to_string(),
159 PropName::Str(s) => s.value.as_str().unwrap_or("").to_string(),
160 PropName::Num(n) => n.value.to_string(),
161 _ => continue,
162 };
163 let prop_val = expr_to_napi_value(raw_env, &kv.value);
164 let mut key_val: napi::sys::napi_value = std::ptr::null_mut();
165 napi::sys::napi_create_string_utf8(
166 raw_env,
167 key.as_ptr() as *const _,
168 key.len() as isize,
169 &mut key_val,
170 );
171 napi::sys::napi_set_property(raw_env, result, key_val, prop_val);
172 }
173 }
174 },
175 Expr::Array(arr) => unsafe {
176 napi::sys::napi_create_array_with_length(raw_env, arr.elems.len(), &mut result);
177 for (i, elem) in arr.elems.iter().enumerate() {
178 let elem_val = match elem {
179 Some(e) => expr_to_napi_value(raw_env, &e.expr),
180 None => {
181 let mut undef: napi::sys::napi_value = std::ptr::null_mut();
182 napi::sys::napi_get_undefined(raw_env, &mut undef);
183 undef
184 },
185 };
186 napi::sys::napi_set_element(raw_env, result, i as u32, elem_val);
187 }
188 },
189 _ => {
190 debug!("Unsupported napi value type: {:#?}.", expr);
191
192 panic!(
193 "Unsupported napi value type: {:?}. If its not enough, please run in debug mode to see more details",
194 expr.get_type(get_default_expr_ctx())
195 );
196 },
197 }
198 result
199}
200
201pub(crate) fn parse_debug_file_path(
203 env: &napi::Env,
204 unknown_val: Unknown,
205) -> napi::Result<JSFunction> {
206 let raw_val = unknown_val.raw();
207 let raw_env = env.raw();
208
209 let mut val_type: napi::sys::napi_valuetype = napi::sys::ValueType::napi_undefined;
210 unsafe { napi::sys::napi_typeof(raw_env, raw_val, &mut val_type) };
211
212 match val_type {
213 napi::sys::ValueType::napi_string => {
214 let mut len = 0;
215 unsafe {
216 napi::sys::napi_get_value_string_utf8(raw_env, raw_val, std::ptr::null_mut(), 0, &mut len);
217 }
218 let mut buf = vec![0u8; len + 1];
219 let mut written = 0;
220 unsafe {
221 napi::sys::napi_get_value_string_utf8(
222 raw_env,
223 raw_val,
224 buf.as_mut_ptr() as *mut _,
225 len + 1,
226 &mut written,
227 );
228 }
229 buf.truncate(written);
230 let s = String::from_utf8(buf).unwrap_or_default();
231 Ok(JSFunction::new(move |_args| create_string_expr(&s)))
232 },
233 napi::sys::ValueType::napi_function => {
234 parse_env_function(env, raw_val).and_then(|entry| match entry {
235 EnvEntry::Function(f) => Ok(f),
236 _ => Err(napi::Error::from_reason(
237 "Expected function from parse_env_function",
238 )),
239 })
240 },
241 _ => Err(napi::Error::from_reason(
242 "debugFilePath must be a string or function",
243 )),
244 }
245}
246
247fn read_napi_string(raw_env: napi::sys::napi_env, value: napi::sys::napi_value) -> String {
248 let mut len = 0;
249 unsafe {
250 napi::sys::napi_get_value_string_utf8(raw_env, value, std::ptr::null_mut(), 0, &mut len);
251 }
252 let mut buf = vec![0u8; len + 1];
253 let mut written = 0;
254 unsafe {
255 napi::sys::napi_get_value_string_utf8(
256 raw_env,
257 value,
258 buf.as_mut_ptr() as *mut _,
259 len + 1,
260 &mut written,
261 );
262 }
263 buf.truncate(written);
264 String::from_utf8(buf).unwrap_or_default()
265}
266
267fn napi_value_to_expr(raw_env: napi::sys::napi_env, value: napi::sys::napi_value) -> Expr {
268 let mut val_type: napi::sys::napi_valuetype = napi::sys::ValueType::napi_undefined;
269 unsafe {
270 napi::sys::napi_typeof(raw_env, value, &mut val_type);
271 }
272
273 match val_type {
274 napi::sys::ValueType::napi_string => create_string_expr(&read_napi_string(raw_env, value)),
275 napi::sys::ValueType::napi_number => {
276 let mut n: f64 = 0.0;
277 unsafe {
278 napi::sys::napi_get_value_double(raw_env, value, &mut n);
279 }
280 create_number_expr(n)
281 },
282 napi::sys::ValueType::napi_boolean => {
283 let mut b = false;
284 unsafe {
285 napi::sys::napi_get_value_bool(raw_env, value, &mut b);
286 }
287 create_bool_expr(b)
288 },
289 napi::sys::ValueType::napi_object => {
290 let mut is_array = false;
291 unsafe {
292 napi::sys::napi_is_array(raw_env, value, &mut is_array);
293 }
294
295 if is_array {
296 let mut length: u32 = 0;
297 unsafe {
298 napi::sys::napi_get_array_length(raw_env, value, &mut length);
299 }
300
301 let elems: Vec<Option<ExprOrSpread>> = (0..length)
302 .map(|i| {
303 let mut elem_val: napi::sys::napi_value = std::ptr::null_mut();
304 unsafe {
305 napi::sys::napi_get_element(raw_env, value, i, &mut elem_val);
306 }
307 Some(ExprOrSpread {
308 spread: None,
309 expr: Box::new(napi_value_to_expr(raw_env, elem_val)),
310 })
311 })
312 .collect();
313
314 create_array_expression(elems)
315 } else {
316 let mut props = Vec::new();
317
318 let mut property_names: napi::sys::napi_value = std::ptr::null_mut();
319 unsafe {
320 napi::sys::napi_get_property_names(raw_env, value, &mut property_names);
321 }
322
323 let mut length: u32 = 0;
324 unsafe {
325 napi::sys::napi_get_array_length(raw_env, property_names, &mut length);
326 }
327
328 for i in 0..length {
329 let mut key_val: napi::sys::napi_value = std::ptr::null_mut();
330 unsafe {
331 napi::sys::napi_get_element(raw_env, property_names, i, &mut key_val);
332 }
333 let key = read_napi_string(raw_env, key_val);
334
335 let mut prop_val: napi::sys::napi_value = std::ptr::null_mut();
336 unsafe {
337 napi::sys::napi_get_property(raw_env, value, key_val, &mut prop_val);
338 }
339
340 props.push(create_key_value_prop(
341 &key,
342 napi_value_to_expr(raw_env, prop_val),
343 ));
344 }
345
346 create_object_expression(props)
347 }
348 },
349 _ => {
350 debug!("Unsupported napi value type: {:#?}.", val_type);
351
352 panic!(
353 "Unsupported napi value type: {:?}. If its not enough, please run in debug mode to see more details",
354 val_type
355 );
356 },
357 }
358}
359
360struct EnvFnRef {
362 ref_ptr: napi::sys::napi_ref,
363 env: napi::sys::napi_env,
364}
365
366impl Drop for EnvFnRef {
367 fn drop(&mut self) {
368 let status = unsafe { napi::sys::napi_delete_reference(self.env, self.ref_ptr) };
369 if status != napi::sys::Status::napi_ok {
370 log::warn!(
371 "Failed to delete napi_ref during cleanup (status: {:?})",
372 status
373 );
374 }
375 }
376}