Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4a4505b
react_server_components transform
SyMind Oct 13, 2025
31c656c
Merge remote-tracking branch 'origin' into react-server-component
SyMind Oct 13, 2025
62ce9de
fix: ReactServerComponents
SyMind Oct 13, 2025
b3b4fcb
ReactServerComponentPlugin
SyMind Oct 15, 2025
1b0c818
chore: merge origin main
SyMind Oct 28, 2025
c87018b
Merge remote-tracking branch 'origin' into react-server-component
SyMind Nov 10, 2025
82925c0
fix: rspack_loader_swc Wtf8Atom
SyMind Nov 11, 2025
58288ed
feat: ReactServerComponentPlugin
SyMind Nov 11, 2025
ac7e626
feat: ServerEntryModules
SyMind Nov 12, 2025
107db20
exports_info
SyMind Nov 13, 2025
99d57f3
temp
SyMind Nov 17, 2025
be58a45
chore: merge origin main
SyMind Nov 17, 2025
4a8c912
feat: import with layer
SyMind Nov 18, 2025
e1015a8
fix: before pass
SyMind Nov 20, 2025
208cc52
export ReactServerComponentsPlugin
SyMind Nov 20, 2025
af4d829
ServerEntries
SyMind Nov 21, 2025
a9253fd
feat: client_entry_loader
SyMind Nov 24, 2025
a2b0457
feat: client compiler
SyMind Nov 25, 2025
2e3accd
feat: traverse_modules
SyMind Nov 25, 2025
fb39548
feat: ClientReferenceManifestPlugin
SyMind Nov 25, 2025
8e5c091
feat: react_server_components layer
SyMind Nov 26, 2025
95a7401
fix ReactClientPlugin
SyMind Nov 26, 2025
68ff4c2
fix: ClientReferenceManifestPlugin
SyMind Nov 26, 2025
cedca6d
refactor: rename to ReactServerPlugin
SyMind Nov 26, 2025
5b0cc98
remove eager
SyMind Nov 27, 2025
4c953cb
feat: collect entry css files
SyMind Nov 27, 2025
137db08
feat: use server-entry
SyMind Dec 1, 2025
b3b67bb
server actions transform
SyMind Dec 1, 2025
31f38e9
feat: action_entry_loader
SyMind Dec 2, 2025
ed7be7f
collect server_actions
SyMind Dec 2, 2025
c88c53b
temp
SyMind Dec 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 42 additions & 2 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ dyn-clone = { version = "1.0.17", default-features = false }
either = { version = "1.15.0", default-features = false }
enum-tag = { version = "0.3.0", default-features = false }
fast-glob = { version = "1.0.0", default-features = false }
form_urlencoded = { version = "1.2.2", default-features = false }
futures = { version = "0.3.31", default-features = false, features = ["std"] }
glob = { version = "0.3.2", default-features = false }
hashlink = { version = "0.10.0", default-features = false }
Expand Down Expand Up @@ -219,6 +220,7 @@ rspack_plugin_progress = { version = "=0.6.3", path = "crates/rs
rspack_plugin_real_content_hash = { version = "=0.6.3", path = "crates/rspack_plugin_real_content_hash", default-features = false }
rspack_plugin_remove_duplicate_modules = { version = "=0.6.3", path = "crates/rspack_plugin_remove_duplicate_modules", default-features = false }
rspack_plugin_remove_empty_chunks = { version = "=0.6.3", path = "crates/rspack_plugin_remove_empty_chunks", default-features = false }
rspack_plugin_rsc = { version = "=0.6.3", path = "crates/rspack_plugin_rsc", default-features = false }
rspack_plugin_rsdoctor = { version = "=0.6.3", path = "crates/rspack_plugin_rsdoctor", default-features = false }
rspack_plugin_rslib = { version = "=0.6.3", path = "crates/rspack_plugin_rslib", default-features = false }
rspack_plugin_rstest = { version = "=0.6.3", path = "crates/rspack_plugin_rstest", default-features = false }
Expand Down
9 changes: 8 additions & 1 deletion crates/node_binding/napi-binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ export declare class JsCompiler {
rebuild(changed_files: string[], removed_files: string[], callback: (err: null | Error) => void): void
close(): Promise<void>
getVirtualFileStore(): VirtualFileStore | null
getCompilerId(): ExternalObject<CompilerId>
}

export declare class JsContextModuleFactoryAfterResolveData {
Expand Down Expand Up @@ -602,7 +603,9 @@ export declare enum BuiltinPluginName {
LazyCompilationPlugin = 'LazyCompilationPlugin',
ModuleInfoHeaderPlugin = 'ModuleInfoHeaderPlugin',
HttpUriPlugin = 'HttpUriPlugin',
CssChunkingPlugin = 'CssChunkingPlugin'
CssChunkingPlugin = 'CssChunkingPlugin',
ReactServerPlugin = 'ReactServerPlugin',
ReactClientPlugin = 'ReactClientPlugin'
}

export declare function cleanupGlobalTrace(): void
Expand Down Expand Up @@ -758,6 +761,10 @@ export interface JsCleanOptions {
keep?: string | RegExp | ((path: string) => boolean)
}

export interface JsClientCompilerHandle {
compile: (() => Promise<undefined>)
}

export interface JsCodegenerationResult {
sources: Record<string, string>
}
Expand Down
1 change: 1 addition & 0 deletions crates/rspack_binding_api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ rspack_paths = { workspace = true }
rspack_plugin_esm_library = { workspace = true }
rspack_plugin_html = { workspace = true }
rspack_plugin_javascript = { workspace = true }
rspack_plugin_rsc = { workspace = true }
rspack_plugin_rsdoctor = { workspace = true }
rspack_plugin_rslib = { workspace = true }
rspack_plugin_rstest = { workspace = true }
Expand Down
7 changes: 7 additions & 0 deletions crates/rspack_binding_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ impl JsCompiler {
rspack_loader_lightningcss::LightningcssLoaderPlugin::new(),
));
plugins.push(Box::new(rspack_loader_swc::SwcLoaderPlugin::new()));
plugins.push(Box::new(rspack_plugin_rsc::ClientEntryLoaderPlugin::new()));
plugins.push(Box::new(rspack_plugin_rsc::ActionEntryLoaderPlugin::new()));
plugins.push(Box::new(
rspack_loader_react_refresh::ReactRefreshLoaderPlugin::new(),
));
Expand Down Expand Up @@ -409,6 +411,11 @@ impl JsCompiler {
.as_ref()
.map(|store| JsVirtualFileStore::new(store.clone()))
}

#[napi]
pub fn get_compiler_id(&self) -> External<CompilerId> {
External::new(self.compiler.id())
}
}

struct RunGuard {
Expand Down
2 changes: 2 additions & 0 deletions crates/rspack_binding_api/src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ mod interceptor;
mod js_cleanup_plugin;
mod js_hooks_plugin;
mod js_loader;
mod rsc;

pub use js_cleanup_plugin::*;
pub use js_hooks_plugin::*;
pub(super) use js_loader::{JsLoaderItem, JsLoaderRspackPlugin, JsLoaderRunnerGetter};
pub mod buildtime_plugins;
pub use interceptor::*;
pub use rsc::{JsClientCompilerHandle, JsReactClientPluginOptions};
84 changes: 84 additions & 0 deletions crates/rspack_binding_api/src/plugins/rsc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use std::sync::Arc;

use napi::{
Env, Status,
bindgen_prelude::{
External, ExternalRef, FromNapiValue, Function, JsObjectValue, Object, Promise, Reference,
WeakReference,
},
threadsafe_function::ThreadsafeFunction,
};
use rspack_core::CompilerId;
use rspack_error::ToStringResultToRspackResultExt;
use rspack_plugin_rsc::{ClientCompilerHandle, ReactClientPluginOptions};

use crate::JsCompiler;

#[napi(object, object_to_js = false)]
pub struct JsClientCompilerHandle {
pub compile: ThreadsafeFunction<(), Promise<()>, (), Status, false, true, 0>,
}

impl From<JsClientCompilerHandle> for ClientCompilerHandle {
fn from(value: JsClientCompilerHandle) -> Self {
let ts_fn = Arc::new(value.compile);

ClientCompilerHandle::new(Box::new(move || {
let ts_fn = ts_fn.clone();
Box::pin(async move {
println!("Calling client compiler compile from JS");
let promise = ts_fn.call_async(()).await.to_rspack_result()?;
promise.await;
println!("Calling client compiler compile from JS end");
Ok(())
})
}))
}
}

type GetServerCompilerId = Box<dyn Fn() -> CompilerId + Sync + Send>;

pub struct JsReactClientPluginOptions {
pub get_server_compiler_id:
ThreadsafeFunction<(), &'static External<CompilerId>, (), Status, false, true, 0>,
}

impl FromNapiValue for JsReactClientPluginOptions {
unsafe fn from_napi_value(
env: napi::sys::napi_env,
napi_val: napi::sys::napi_value,
) -> napi::Result<Self> {
let obj = unsafe { Object::from_napi_value(env, napi_val)? };

let js_fn = obj.get_named_property::<Function<'static, (), &'static External<CompilerId>>>(
"getServerCompilerId",
)?;

let ts_fn = js_fn
.build_threadsafe_function::<()>()
.callee_handled::<false>()
.max_queue_size()
.weak::<true>()
.build()?;

Ok(Self {
get_server_compiler_id: ts_fn,
})
}
}

impl From<JsReactClientPluginOptions> for ReactClientPluginOptions {
fn from(value: JsReactClientPluginOptions) -> Self {
let ts_fn = Arc::new(value.get_server_compiler_id);

Self {
get_server_compiler_id: Box::new(move || {
let ts_fn = ts_fn.clone();
Box::pin(async move {
let external = ts_fn.call_async(()).await.to_rspack_result()?;
Ok(**external)
})
}),
}
}
}
22 changes: 18 additions & 4 deletions crates/rspack_binding_api/src/raw_options/raw_builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ use rspack_plugin_no_emit_on_errors::NoEmitOnErrorsPlugin;
use rspack_plugin_real_content_hash::RealContentHashPlugin;
use rspack_plugin_remove_duplicate_modules::RemoveDuplicateModulesPlugin;
use rspack_plugin_remove_empty_chunks::RemoveEmptyChunksPlugin;
use rspack_plugin_rsc::{ReactClientPlugin, ReactServerPlugin};
use rspack_plugin_rslib::RslibPlugin;
use rspack_plugin_runtime::{
ArrayPushCallbackChunkFormatPlugin, BundlerInfoPlugin, ChunkPrefetchPreloadPlugin,
Expand Down Expand Up @@ -126,7 +127,9 @@ use self::{
};
use crate::{
options::entry::JsEntryPluginOptions,
plugins::{JsLoaderRspackPlugin, JsLoaderRunnerGetter},
plugins::{
JsClientCompilerHandle, JsLoaderRspackPlugin, JsLoaderRunnerGetter, JsReactClientPluginOptions,
},
raw_options::{
RawDynamicEntryPluginOptions, RawEvalDevToolModulePluginOptions, RawExternalItemWrapper,
RawExternalsPluginOptions, RawHttpExternalsRspackPluginOptions, RawSplitChunksOptions,
Expand Down Expand Up @@ -236,6 +239,10 @@ pub enum BuiltinPluginName {
ModuleInfoHeaderPlugin,
HttpUriPlugin,
CssChunkingPlugin,

// react server components
ReactServerPlugin,
ReactClientPlugin,
}

#[doc(hidden)]
Expand Down Expand Up @@ -291,7 +298,6 @@ impl<'a> BuiltinPlugin<'a> {
}
};
match name {
// webpack also have these plugins
BuiltinPluginName::DefinePlugin => {
let plugin = DefinePlugin::new(
downcast_into(self.options)
Expand Down Expand Up @@ -649,8 +655,6 @@ impl<'a> BuiltinPlugin<'a> {
.boxed();
plugins.push(plugin)
}

// rspack specific plugins
BuiltinPluginName::HttpExternalsRspackPlugin => {
let plugin_options = downcast_into::<RawHttpExternalsRspackPluginOptions>(self.options)
.map_err(|report| napi::Error::from_reason(report.to_string()))?;
Expand Down Expand Up @@ -833,6 +837,16 @@ impl<'a> BuiltinPlugin<'a> {
.map_err(|report| napi::Error::from_reason(report.to_string()))?;
plugins.push(CssChunkingPlugin::new(options.into()).boxed());
}
BuiltinPluginName::ReactServerPlugin => {
let options = downcast_into::<JsClientCompilerHandle>(self.options)
.map_err(|report| napi::Error::from_reason(report.to_string()))?;
plugins.push(ReactServerPlugin::new(options.into()).boxed());
}
BuiltinPluginName::ReactClientPlugin => {
let options = downcast_into::<JsReactClientPluginOptions>(self.options)
.map_err(|report| napi::Error::from_reason(report.to_string()))?;
plugins.push(ReactClientPlugin::new(options.into()).boxed());
}
}
Ok(())
}
Expand Down
12 changes: 9 additions & 3 deletions crates/rspack_binding_api/src/swc.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use std::sync::Arc;

use napi::bindgen_prelude::within_runtime_if_available;
use rspack_javascript_compiler::{
JavaScriptCompiler, TransformOutput as CompilerTransformOutput, minify::JsMinifyOptions,
transform::SwcOptions,
};
use rspack_util::source_map::SourceMapKind;
use swc_core::{base::config::SourceMapsConfig, ecma::ast::noop_pass};
use swc_core::{
base::config::SourceMapsConfig, common::comments::SingleThreadedComments, ecma::ast::noop_pass,
};

#[napi(object)]
pub struct TransformOutput {
Expand Down Expand Up @@ -52,14 +56,16 @@ fn _transform(source: String, options: String) -> napi::Result<TransformOutput>
}

let compiler = JavaScriptCompiler::new();
let comments = Arc::new(SingleThreadedComments::default());
let module_source_map_kind = _to_source_map_kind(options.source_maps.clone());

compiler
.transform(
source,
Some(swc_core::common::FileName::Real(
Some(Arc::new(swc_core::common::FileName::Real(
options.filename.clone().into(),
)),
))),
comments,
options,
Some(module_source_map_kind),
|_, _| {},
Expand Down
Loading