rspack_binding_api 0.100.0

Rspack shared binding API
Documentation
use std::sync::Arc;

use futures::future::BoxFuture;
use napi::{
  Env, Status,
  bindgen_prelude::{
    ClassInstance, Either3, External, ExternalRef, FromNapiValue, Function, JsObjectValue, Null,
    Object, Promise, Reference, Undefined, WeakReference,
  },
  threadsafe_function::ThreadsafeFunction,
};
use once_cell::unsync::OnceCell;
use rspack_core::{Compiler, CompilerId};
use rspack_error::ToStringResultToRspackResultExt;
use rspack_plugin_rsc::{Coordinator, OnManifest, RscClientPluginOptions, RscServerPluginOptions};

use crate::JsCompiler;

type InvalidateTsFn = Arc<ThreadsafeFunction<(), (), (), Status, false, true, 0>>;

#[napi]
pub struct JsCoordinator {
  i: Arc<Coordinator>,
}

#[napi]
impl JsCoordinator {
  #[napi(constructor)]
  pub fn new(
    get_server_compiler_id_js_fn: Function<'static, (), &'static External<CompilerId>>,
  ) -> napi::Result<Self> {
    let get_server_compiler_id = {
      let ts_fn = Arc::new(
        get_server_compiler_id_js_fn
          .build_threadsafe_function::<()>()
          .callee_handled::<false>()
          .max_queue_size::<0>()
          .weak::<true>()
          .build()?,
      );
      Box::new(
        move || -> BoxFuture<'static, rspack_error::Result<CompilerId>> {
          let ts_fn = ts_fn.clone();
          Box::pin(async move {
            let external = ts_fn.call_async(()).await.to_rspack_result()?;
            Ok(**external)
          })
        },
      )
    };

    Ok(Self {
      i: Arc::new(Coordinator::new(get_server_compiler_id)),
    })
  }
}

impl From<&JsCoordinator> for Arc<Coordinator> {
  fn from(value: &JsCoordinator) -> Self {
    value.i.clone()
  }
}

#[napi(object, object_to_js = false)]
pub struct JsRscClientPluginOptions<'a> {
  pub coordinator: ClassInstance<'a, JsCoordinator>,
}

impl From<&JsRscClientPluginOptions<'_>> for RscClientPluginOptions {
  fn from(value: &JsRscClientPluginOptions) -> Self {
    Self {
      coordinator: value.coordinator.i.clone(),
    }
  }
}

#[napi(object, object_to_js = false)]
pub struct JsRscServerPluginOptions<'a> {
  pub coordinator: ClassInstance<'a, JsCoordinator>,
  pub on_server_component_changes: Option<Either3<Function<'static, (), ()>, Undefined, Null>>,
  pub on_manifest: Option<Either3<Function<'static, String, Promise<()>>, Undefined, Null>>,
}

impl TryFrom<&JsRscServerPluginOptions<'_>> for RscServerPluginOptions {
  type Error = napi::Error;

  fn try_from(value: &JsRscServerPluginOptions) -> napi::Result<Self> {
    let on_server_component_changes: Option<
      Box<dyn Fn() -> BoxFuture<'static, rspack_error::Result<()>> + Sync + Send>,
    > = match value.on_server_component_changes {
      Some(Either3::A(js_fn)) => {
        let ts_fn = Arc::new(
          js_fn
            .build_threadsafe_function::<()>()
            .callee_handled::<false>()
            .max_queue_size::<0>()
            .weak::<true>()
            .build()?,
        );
        Some(Box::new(
          move || -> BoxFuture<'static, rspack_error::Result<()>> {
            let ts_fn = ts_fn.clone();
            Box::pin(async move { ts_fn.call_async(()).await.to_rspack_result() })
          },
        ))
      }
      _ => None,
    };

    let on_manifest: Option<OnManifest> = match &value.on_manifest {
      Some(Either3::A(js_fn)) => {
        let ts_fn = Arc::new(
          js_fn
            .build_threadsafe_function::<(String)>()
            .callee_handled::<false>()
            .max_queue_size::<0>()
            .weak::<true>()
            .build()?,
        );
        Some(Box::new(move |json: String| {
          let ts_fn = ts_fn.clone();
          Box::pin(async move {
            ts_fn
              .call_async(json)
              .await
              .to_rspack_result()?
              .await
              .to_rspack_result()
          })
        }))
      }
      _ => None,
    };

    Ok(Self {
      coordinator: value.coordinator.i.clone(),
      on_server_component_changes,
      on_manifest,
    })
  }
}