#[allow(unused_imports)]
use napi::bindgen_prelude::{JsObjectValue, ToNapiValue, Unknown, Object};
#[allow(unused_imports)]
use napi::JsValue;
fn nodecontext_to_js_object<'e>(
env: &'e napi::Env,
ctx: &{{ core_crate }}::visitor::NodeContext,
) -> napi::Result<napi::bindgen_prelude::Object<'e>> {
let mut obj = napi::bindgen_prelude::Object::new(env)?;
obj.set_named_property("nodeType", env.create_string(&format!("{:?}", ctx.node_type))?)?;
obj.set_named_property("tagName", env.create_string(&ctx.tag_name)?)?;
obj.set_named_property("depth", env.create_uint32(ctx.depth as u32)?)?;
obj.set_named_property("indexInParent", env.create_uint32(ctx.index_in_parent as u32)?)?;
obj.set_named_property("isInline", ctx.is_inline)?;
let parent_tag = match &ctx.parent_tag {
Some(s) => env.create_string(s)?.to_unknown(),
None => {
// SAFETY: napi_get_null returns a valid napi_value for the given env.
let raw = unsafe { napi::bindgen_prelude::ToNapiValue::to_napi_value(env.raw(), napi::bindgen_prelude::Null)? };
unsafe { napi::bindgen_prelude::Unknown::from_raw_unchecked(env.raw(), raw) }
}
};
obj.set_named_property("parentTag", parent_tag)?;
let mut attrs = napi::bindgen_prelude::Object::new(env)?;
for (k, v) in &ctx.attributes {
attrs.set_named_property(k, env.create_string(v)?)?;
}
obj.set_named_property("attributes", attrs)?;
Ok(obj)
}
pub struct {{ struct_name }} {
obj: napi::bindgen_prelude::Object<'static>,
env: napi::Env,
}
impl std::fmt::Debug for {{ struct_name }} {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{ struct_name }}")
}
}
impl {{ struct_name }} {
pub fn new(js_obj: napi::bindgen_prelude::Object<'_>) -> Self {
// Extract and cache the napi_env from the Object BEFORE transmuting.
// We do this once and cache it to avoid repeated unsafe transmute_copy calls.
// SAFETY: Object<'_> is 3 pointer-sized words; first word is napi_env.
let env = unsafe {
let raw: [*mut std::ffi::c_void; 3] = std::mem::transmute_copy(&js_obj);
napi::Env::from_raw(raw[0] as napi::sys::napi_env)
};
// SAFETY: The JS object is owned by the Node.js runtime and lives for
// the duration of the enclosing #[napi] call. The bridge is only used
// synchronously during that same call, so 'static is safe here.
let obj: napi::bindgen_prelude::Object<'static> = unsafe { std::mem::transmute(js_obj) };
Self { obj, env }
}
fn env(&self) -> napi::Env {
self.env
}
}
// SAFETY: `napi::bindgen_prelude::Object<'static>` and `napi::Env` are not
// automatically `Send` because they wrap raw V8/Node.js handles tied to a
// specific isolate/thread. This bridge is only ever invoked synchronously from
// the same thread that constructed it (the #[napi] call frame) — the `Arc<Mutex>`
// wrapper ensures exclusive access and the caller must not move the owning options
// struct across threads while the bridge is alive. Misuse (calling convert() from
// a different thread than the one that created the visitor) is the caller's
// responsibility and will trigger undefined behaviour in the V8 runtime.
unsafe impl Send for {{ struct_name }} {}
// SAFETY: see Send impl above.
unsafe impl Sync for {{ struct_name }} {}
impl {{ trait_path }} for {{ struct_name }} {
{{ method_impls }}
}