Skip to content

Commit

Permalink
feat: add plugin adapter (#53)
Browse files Browse the repository at this point in the history
* feat: add plugin adapter

* chore: add bundler with_plugins
  • Loading branch information
underfin authored Oct 15, 2023
1 parent 9391f16 commit 6438255
Show file tree
Hide file tree
Showing 20 changed files with 494 additions and 94 deletions.
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/rolldown/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ rolldown_common = { path = "../rolldown_common" }
rolldown_tracing = { path = "../rolldown_tracing" }
rolldown_utils = { path = "../rolldown_utils" }
rolldown_resolver = { path = "../rolldown_resolver" }
rolldown_plugin = { path = "../rolldown_plugin" }
rolldown_error = { path = "../rolldown_error" }
rolldown_oxc = { path = "../rolldown_oxc" }
derivative = { workspace = true }
Expand Down
12 changes: 12 additions & 0 deletions crates/rolldown/src/bundler/bundler.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use rolldown_plugin::BoxPlugin;
use sugar_path::AsPath;

use super::{
Expand All @@ -12,6 +13,7 @@ use crate::{bundler::bundle::bundle::Bundle, InputOptions};

pub struct Bundler {
input_options: NormalizedInputOptions,
_plugins: Vec<BoxPlugin>,
}

impl Bundler {
Expand All @@ -20,6 +22,16 @@ impl Bundler {
let normalized = NormalizedInputOptions::from_input_options(input_options);
Self {
input_options: normalized,
_plugins: vec![],
}
}

pub fn with_plugins(input_options: InputOptions, plugins: Vec<BoxPlugin>) -> Self {
// rolldown_tracing::enable_tracing_on_demand();
let normalized = NormalizedInputOptions::from_input_options(input_options);
Self {
input_options: normalized,
_plugins: plugins,
}
}

Expand Down
2 changes: 2 additions & 0 deletions crates/rolldown_error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ mod error_kind;
mod errors;
mod utils;
pub use crate::{error::Error, errors::Errors};

pub type Result<T> = std::result::Result<T, Error>;
1 change: 1 addition & 0 deletions crates/rolldown_node_binding/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ itertools = { workspace = true }
napi = { version = "2.11.1", features = ["full"] }
napi-derive = { version = "2.11.0" }
rolldown = { path = "../rolldown" }
rolldown_plugin = { path = "../rolldown_plugin" }
rolldown_error = { path = "../rolldown_error" }
rolldown_tracing = { path = "../rolldown_tracing" }
rustc-hash = { workspace = true }
Expand Down
17 changes: 17 additions & 0 deletions crates/rolldown_node_binding/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,25 @@

/* auto-generated by NAPI-RS */

export interface PluginOptions {
name: string
resolveId?: (
specifier: string,
importer?: string,
) => Promise<undefined | ResolveIdResult>
load?: (id: string) => Promise<undefined | SourceResult>
transform?: (id: string, code: string) => Promise<undefined | SourceResult>
}
export interface ResolveIdResult {
id: string
external?: boolean
}
export interface SourceResult {
code: string
}
export interface InputOptions {
input: Record<string, string>
plugins: Array<PluginOptions>
preserveSymlinks: boolean
shimMissingExports: boolean
treeshake?: boolean
Expand Down
4 changes: 2 additions & 2 deletions crates/rolldown_node_binding/src/bundler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ impl Bundler {
impl Bundler {
pub fn new_impl(env: Env, input_opts: InputOptions) -> napi::Result<Self> {
NAPI_ENV.set(&env, || {
let input_opts = resolve_input_options(input_opts)?;
let (input_opts, plugins) = resolve_input_options(input_opts)?;
Ok(Bundler {
inner: Mutex::new(NativeBundler::new(input_opts)),
inner: Mutex::new(NativeBundler::with_plugins(input_opts, plugins)),
})
})
}
Expand Down
48 changes: 32 additions & 16 deletions crates/rolldown_node_binding/src/options/input_options/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use std::{collections::HashMap, path::PathBuf};

mod plugin;
mod plugin_adapter;
use napi_derive::*;
use serde::Deserialize;

use crate::options::input_options::plugin_adapter::JsAdapterPlugin;

use self::plugin::PluginOptions;

#[napi(object)]
#[derive(Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
Expand All @@ -27,7 +32,7 @@ pub struct InputOptions {
// moduleContext?: ((id: string) => string | null | void) | { [id: string]: string };
// onwarn?: WarningHandlerWithDefault;
// perf?: boolean;
// pub plugins: Vec<BuildPluginOption>,
pub plugins: Vec<PluginOptions>,
// preserveEntrySignatures?: PreserveEntrySignaturesOption;
// /** @deprecated Use the "preserveModules" output option instead. */
// preserveModules?: boolean;
Expand All @@ -42,21 +47,32 @@ pub struct InputOptions {
// pub builtins: BuiltinsOptions,
}

pub fn resolve_input_options(opts: InputOptions) -> napi::Result<rolldown::InputOptions> {
pub fn resolve_input_options(
opts: InputOptions,
) -> napi::Result<(rolldown::InputOptions, Vec<rolldown_plugin::BoxPlugin>)> {
let cwd = PathBuf::from(opts.cwd.clone());
assert!(cwd != PathBuf::from("/"), "{:#?}", opts);

Ok(rolldown::InputOptions {
input: Some(
opts
.input
.into_iter()
.map(|(name, import)| rolldown::InputItem {
name: Some(name),
import,
})
.collect::<Vec<_>>(),
),
cwd: Some(cwd),
})
let plugins = opts
.plugins
.into_iter()
.map(JsAdapterPlugin::new_boxed)
.collect::<napi::Result<Vec<_>>>()?;

Ok((
rolldown::InputOptions {
input: Some(
opts
.input
.into_iter()
.map(|(name, import)| rolldown::InputItem {
name: Some(name),
import,
})
.collect::<Vec<_>>(),
),
cwd: Some(cwd),
},
plugins,
))
}
60 changes: 60 additions & 0 deletions crates/rolldown_node_binding/src/options/input_options/plugin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use derivative::Derivative;
use napi::JsFunction;
use serde::Deserialize;

#[napi_derive::napi(object)]
#[derive(Deserialize, Default, Derivative)]
#[serde(rename_all = "camelCase")]
#[derivative(Debug)]
pub struct PluginOptions {
pub name: String,

#[derivative(Debug = "ignore")]
#[serde(skip_deserializing)]
#[napi(
ts_type = "(specifier: string, importer?: string) => Promise<undefined | ResolveIdResult>"
)]
pub resolve_id: Option<JsFunction>,

#[derivative(Debug = "ignore")]
#[serde(skip_deserializing)]
#[napi(ts_type = "(id: string) => Promise<undefined | SourceResult>")]
pub load: Option<JsFunction>,

#[derivative(Debug = "ignore")]
#[serde(skip_deserializing)]
#[napi(ts_type = "(id: string, code: string) => Promise<undefined | SourceResult>")]
pub transform: Option<JsFunction>,
}

#[napi_derive::napi(object)]
#[derive(Deserialize, Default, Derivative)]
#[serde(rename_all = "camelCase")]
#[derivative(Debug)]
pub struct ResolveIdResult {
pub id: String,
pub external: Option<bool>,
}

impl From<ResolveIdResult> for rolldown_plugin::ResolveIdResult {
fn from(value: ResolveIdResult) -> Self {
rolldown_plugin::ResolveIdResult {
id: value.id,
external: value.external,
}
}
}

#[napi_derive::napi(object)]
#[derive(Deserialize, Default, Derivative)]
#[serde(rename_all = "camelCase")]
#[derivative(Debug)]
pub struct SourceResult {
pub code: String,
}

impl From<SourceResult> for rolldown_plugin::SourceResult {
fn from(value: SourceResult) -> Self {
rolldown_plugin::SourceResult { code: value.code }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use crate::utils::napi_error_ext::NapiErrorExt;
use crate::utils::JsCallback;
use derivative::Derivative;
use rolldown_plugin::{BoxPlugin, Plugin, PluginName};

use super::plugin::{PluginOptions, ResolveIdResult, SourceResult};

pub type ResolveIdCallback = JsCallback<(String, Option<String>), Option<ResolveIdResult>>;
pub type LoadCallback = JsCallback<(String, Option<String>), Option<SourceResult>>;
pub type TransformCallback = JsCallback<(String, String), Option<SourceResult>>;

#[derive(Derivative)]
#[derivative(Debug)]
pub struct JsAdapterPlugin {
pub name: String,
#[derivative(Debug = "ignore")]
resolve_id_fn: Option<ResolveIdCallback>,
#[derivative(Debug = "ignore")]
load_fn: Option<LoadCallback>,
#[derivative(Debug = "ignore")]
transform_fn: Option<TransformCallback>,
}

impl JsAdapterPlugin {
pub fn new(option: PluginOptions) -> napi::Result<Self> {
let resolve_id_fn = option
.resolve_id
.as_ref()
.map(ResolveIdCallback::new)
.transpose()?;
let load_fn = option
.resolve_id
.as_ref()
.map(LoadCallback::new)
.transpose()?;
let transform_fn = option
.transform
.as_ref()
.map(TransformCallback::new)
.transpose()?;
Ok(JsAdapterPlugin {
name: option.name,
resolve_id_fn,
load_fn,
transform_fn,
})
}

pub fn new_boxed(option: PluginOptions) -> napi::Result<BoxPlugin> {
Ok(Box::new(Self::new(option)?))
}
}

#[async_trait::async_trait]
impl Plugin for JsAdapterPlugin {
fn name(&self) -> PluginName {
std::borrow::Cow::Borrowed(&self.name)
}

async fn resolve_id(
&self,
_ctx: &mut rolldown_plugin::Context,
args: &rolldown_plugin::ResolveIdArgs,
) -> rolldown_plugin::ResolveIdReturn {
if let Some(cb) = &self.resolve_id_fn {
let res = cb
.call_async((
args.source.to_string(),
args.importer.map(|s| s.to_string()),
))
.await
.map_err(|e| e.into_bundle_error())?;

Ok(res.map(Into::into))
} else {
Ok(None)
}
}

async fn load(
&self,
_ctx: &mut rolldown_plugin::Context,
args: &rolldown_plugin::LoadArgs,
) -> rolldown_plugin::LoadReturn {
if let Some(cb) = &self.load_fn {
let res = cb
.call_async((args.id.to_string(), None))
.await
.map_err(|e| e.into_bundle_error())?;
Ok(res.map(Into::into))
} else {
Ok(None)
}
}

async fn transform(
&self,
_ctx: &mut rolldown_plugin::Context,
args: &rolldown_plugin::TransformArgs,
) -> rolldown_plugin::TransformReturn {
if let Some(cb) = &self.transform_fn {
let res = cb
.call_async((args.code.to_string(), args.id.to_string()))
.await
.map_err(|e| e.into_bundle_error())?;
Ok(res.map(Into::into))
} else {
Ok(None)
}
}
}
2 changes: 1 addition & 1 deletion crates/rolldown_node_binding/src/utils/js_callback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl<Args: JsCallbackArgs + Debug, Ret: JsCallbackRet> JsCallback<Args, Ret> {
}

/// This method is already handle case return Promise<Ret>
pub(crate) async fn _call_async(&self, args: Args) -> napi::Result<Ret> {
pub(crate) async fn call_async(&self, args: Args) -> napi::Result<Ret> {
let ret: Either<Ret, Promise<Ret>> = self.ts_fn.call_async(args).await?;

match ret {
Expand Down
2 changes: 1 addition & 1 deletion crates/rolldown_node_binding/src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
mod napi_error_ext;
pub mod napi_error_ext;
use std::sync::atomic::AtomicBool;

use napi::Env;
Expand Down
15 changes: 15 additions & 0 deletions crates/rolldown_plugin/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
description = "rolldown_plugin"
edition = "2021"
license = "Apache-2.0"
name = "rolldown_plugin"
version = "0.0.1"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = { workspace = true }
async-trait = { workspace = true }
rustc-hash = { workspace = true }
rolldown_common = { version = "0.0.1", path = "../rolldown_common" }
rolldown_error = { version = "0.0.1", path = "../rolldown_error" }
Loading

0 comments on commit 6438255

Please sign in to comment.