Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React Server Components #10043

Merged
merged 41 commits into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
22e4dbe
Evaluate import attributes
devongovett Dec 1, 2024
3fc1a7f
Add react-client and react-server environments
devongovett Dec 1, 2024
06a5a4f
Support "source" exports condition
devongovett Dec 1, 2024
4b7348c
Handle "use client" and "use server" directives
devongovett Dec 1, 2024
acec228
Support "use server-entry" to create isolated bundle groups
devongovett Dec 1, 2024
8cf1718
HMR
devongovett Dec 1, 2024
67353f9
Fix scope hoisted builds
devongovett Dec 2, 2024
fc7110b
Add import.meta.distDir and import.meta.publicUrl
devongovett Dec 2, 2024
a0b8bb4
RSC runtime
devongovett Dec 6, 2024
33eab53
RSC example
devongovett Dec 6, 2024
51a99fa
Register server references in client bundles correctly
devongovett Dec 8, 2024
69ab107
Don't execute module in HMR that haven't already loaded
devongovett Dec 8, 2024
c8cd3a9
Fix dependency collector bug
devongovett Dec 8, 2024
b8b44b6
Ignore of ws module is not available in HMR runtime
devongovett Dec 8, 2024
8aac352
Fix bundle graph check for async bundles
devongovett Dec 8, 2024
a160634
Fix library bundler for bundle graph change
devongovett Dec 9, 2024
86d5195
Handle internalized async dependencies
devongovett Dec 9, 2024
4eb2f66
Don't remove type=module in development to avoid multiple copies of a…
devongovett Dec 9, 2024
9ea9318
prettier
devongovett Dec 9, 2024
a256737
Resolve react relative to app
devongovett Dec 9, 2024
24f9c46
Use react-server-dom-parcel from npm
devongovett Dec 9, 2024
61f02c2
Fix default node engines in test
devongovett Dec 9, 2024
561beac
Allow parallel bundles with different contexts from the root bundle b…
devongovett Dec 10, 2024
d9b984f
Don't transpile dynamic imports in TS plugins
devongovett Dec 10, 2024
215dd5e
Remove error that "Only browser targets are supported in serve mode"
devongovett Dec 10, 2024
0fa0008
Use parcelRequire.load to load bundles
devongovett Dec 10, 2024
c0b0ee6
lint
devongovett Dec 10, 2024
e0a86c2
fix test
devongovett Dec 10, 2024
b1c3ad4
Bump react-server-dom-parcel
devongovett Dec 10, 2024
0b82b13
format
devongovett Dec 10, 2024
f12dac0
Bump react to 19
devongovett Dec 16, 2024
07f24a8
Replace typeof process with 'undefined' to avoid adding polyfill for …
devongovett Dec 16, 2024
1f7c98d
normalize publicUrl
devongovett Dec 16, 2024
1cb5ae5
treat context changes async bundles
devongovett Dec 16, 2024
6c9a184
refactor to let react insert scripts
devongovett Dec 16, 2024
9f297b1
add tests
devongovett Dec 16, 2024
6a13048
Improve inference for targets in serve mode
devongovett Dec 22, 2024
4e7c3a4
Bundle node_modules in the react-server context for now
devongovett Dec 22, 2024
4e0b155
fixes
devongovett Dec 22, 2024
93be6cf
Merge branch 'v2' of github.com:parcel-bundler/parcel into rsc2
devongovett Dec 22, 2024
1f5f005
update versions
devongovett Dec 22, 2024
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
1 change: 1 addition & 0 deletions Cargo.lock

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

140 changes: 78 additions & 62 deletions crates/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![deny(unused_crate_dependencies)]

use serde::{Deserialize, Serialize};
use std::{
collections::{HashMap, HashSet},
sync::Arc,
Expand Down Expand Up @@ -44,9 +45,8 @@ pub type MacroCallback =
pub struct Macros<'a> {
/// Mapping of imported identifiers to import metadata.
macros: HashMap<Id, MacroImport>,
constants: HashMap<Id, Result<JsValue, Span>>,
evaluator: Evaluator<'a>,
callback: MacroCallback,
source_map: &'a SourceMap,
errors: &'a mut Vec<MacroError>,
load_errors: HashSet<String>,
assignment_span: Option<Span>,
Expand All @@ -70,10 +70,9 @@ impl<'a> Macros<'a> {
) -> Self {
Macros {
macros: HashMap::new(),
constants: HashMap::new(),
evaluator: Evaluator::new(source_map),
load_errors: HashSet::new(),
callback,
source_map,
errors,
assignment_span: None,
in_call: false,
Expand Down Expand Up @@ -137,7 +136,7 @@ impl<'a> Macros<'a> {
// Try to statically evaluate all of the function arguments.
let mut args = Vec::with_capacity(call.args.len());
for arg in &call.args {
match self.eval(&*arg.expr) {
match self.evaluator.eval(&*arg.expr) {
Ok(val) => {
if arg.spread.is_none() {
args.push(val);
Expand All @@ -154,13 +153,13 @@ impl<'a> Macros<'a> {
}

// If that was successful, call the function callback (on the JS thread).
let loc = self.source_map.lookup_char_pos(call.span.lo);
let loc = self.evaluator.source_map.lookup_char_pos(call.span.lo);
let loc = Location {
line: loc.line as u32,
col: loc.col_display as u32,
};
match (self.callback)(src.clone(), export, args, loc) {
Ok(val) => Ok(self.value_to_expr(val)?),
Ok(val) => Ok(self.evaluator.value_to_expr(val)?),
Err(err) => match err {
MacroError::LoadError(err, _) => {
self.load_errors.insert(src);
Expand Down Expand Up @@ -224,7 +223,7 @@ impl<'a> Fold for Macros<'a> {
let imported = match &member.prop {
MemberProp::Ident(id) => id.sym.to_string(),
MemberProp::Computed(s) => {
if let Ok(JsValue::String(s)) = self.eval(&s.expr) {
if let Ok(JsValue::String(s)) = self.evaluator.eval(&s.expr) {
s
} else {
break 'block;
Expand Down Expand Up @@ -263,8 +262,8 @@ impl<'a> Fold for Macros<'a> {
if node.kind == VarDeclKind::Const {
for decl in &node.decls {
if let Some(expr) = &decl.init {
let val = self.eval(&*expr);
self.eval_pat(val, &decl.name);
let val = self.evaluator.eval(&*expr);
self.evaluator.eval_pat(val, &decl.name);
}
}
}
Expand All @@ -286,7 +285,7 @@ impl<'a> Fold for Macros<'a> {
// Error when re-assigning a property of a constant that's used in a macro.
let node = node.fold_children_with(self);
if let Expr::Ident(id) = &*node.obj {
if let Some(constant) = self.constants.get_mut(&id.to_id()) {
if let Some(constant) = self.evaluator.constants.get_mut(&id.to_id()) {
if constant.is_ok() {
*constant = Err(assignment_span.clone());
}
Expand All @@ -299,8 +298,9 @@ impl<'a> Fold for Macros<'a> {
// If the member expression evaluates to an object, continue traversing so we error in fold_ident.
// Otherwise, return early to allow other properties to be accessed without error.
let value = self
.evaluator
.eval(&*node.obj)
.and_then(|obj| self.eval_member_prop(obj, &node));
.and_then(|obj| self.evaluator.eval_member_prop(obj, &node));
if !matches!(
value,
Err(..) | Ok(JsValue::Object(..) | JsValue::Array(..))
Expand All @@ -314,7 +314,7 @@ impl<'a> Fold for Macros<'a> {

fn fold_ident(&mut self, node: Ident) -> Ident {
if self.in_call {
if let Some(constant) = self.constants.get_mut(&node.to_id()) {
if let Some(constant) = self.evaluator.constants.get_mut(&node.to_id()) {
if matches!(constant, Ok(JsValue::Object(..) | JsValue::Array(..))) {
// Mark access to constant object inside a call as an error since it could potentially be mutated.
*constant = Err(node.span.clone());
Expand Down Expand Up @@ -362,7 +362,8 @@ fn handle_error(result: Result<Expr, MacroError>, errors: &mut Vec<MacroError>)
}

/// A type that represents a basic JS value.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum JsValue {
Undefined,
Null,
Expand All @@ -375,9 +376,21 @@ pub enum JsValue {
Function(String),
}

impl<'a> Macros<'a> {
pub struct Evaluator<'a> {
constants: HashMap<Id, Result<JsValue, Span>>,
source_map: &'a SourceMap,
}

impl<'a> Evaluator<'a> {
pub fn new(source_map: &'a SourceMap) -> Evaluator<'_> {
Evaluator {
constants: HashMap::new(),
source_map,
}
}

/// Statically evaluate a JS expression to a value, if possible.
fn eval(&self, expr: &Expr) -> Result<JsValue, Span> {
pub fn eval(&self, expr: &Expr) -> Result<JsValue, Span> {
match expr.unwrap_parens() {
Expr::Lit(lit) => match lit {
Lit::Null(_) => Ok(JsValue::Null),
Expand Down Expand Up @@ -437,49 +450,7 @@ impl<'a> Macros<'a> {
}
Ok(JsValue::Array(res))
}
Expr::Object(obj) => {
let mut res = IndexMap::with_capacity(obj.props.len());
for prop in &obj.props {
match prop {
PropOrSpread::Prop(prop) => match &**prop {
Prop::KeyValue(kv) => {
let v = self.eval(&*kv.value)?;
let k = match &kv.key {
PropName::Ident(IdentName { sym, .. })
| PropName::Str(Str { value: sym, .. }) => sym.to_string(),
PropName::Num(n) => n.value.to_string(),
PropName::Computed(c) => match self.eval(&*c.expr) {
Err(e) => return Err(e),
Ok(JsValue::String(s)) => s,
Ok(JsValue::Number(n)) => n.to_string(),
Ok(JsValue::Bool(b)) => b.to_string(),
_ => return Err(c.span),
},
PropName::BigInt(v) => return Err(v.span),
};

res.insert(k.to_string(), v);
}
Prop::Shorthand(s) => {
if let Some(val) = self.constants.get(&s.to_id()) {
res.insert(s.sym.to_string(), val.clone()?);
} else {
return Err(s.span);
}
}
_ => return Err(obj.span),
},
PropOrSpread::Spread(spread) => {
let v = self.eval(&*spread.expr)?;
match v {
JsValue::Object(o) => res.extend(o),
_ => return Err(obj.span),
}
}
}
}
Ok(JsValue::Object(res))
}
Expr::Object(obj) => self.eval_object(obj),
Expr::Bin(bin) => match (bin.op, self.eval(&*bin.left), self.eval(&*bin.right)) {
(BinaryOp::Add, Ok(JsValue::String(a)), Ok(JsValue::String(b))) => {
Ok(JsValue::String(format!("{}{}", a, b)))
Expand Down Expand Up @@ -676,7 +647,52 @@ impl<'a> Macros<'a> {
}
}

fn eval_member_prop(&self, obj: JsValue, member: &MemberExpr) -> Result<JsValue, Span> {
pub fn eval_object(&self, obj: &ObjectLit) -> Result<JsValue, Span> {
let mut res = IndexMap::with_capacity(obj.props.len());
for prop in &obj.props {
match prop {
PropOrSpread::Prop(prop) => match &**prop {
Prop::KeyValue(kv) => {
let v = self.eval(&*kv.value)?;
let k = match &kv.key {
PropName::Ident(IdentName { sym, .. }) | PropName::Str(Str { value: sym, .. }) => {
sym.to_string()
}
PropName::Num(n) => n.value.to_string(),
PropName::Computed(c) => match self.eval(&*c.expr) {
Err(e) => return Err(e),
Ok(JsValue::String(s)) => s,
Ok(JsValue::Number(n)) => n.to_string(),
Ok(JsValue::Bool(b)) => b.to_string(),
_ => return Err(c.span),
},
PropName::BigInt(v) => return Err(v.span),
};

res.insert(k.to_string(), v);
}
Prop::Shorthand(s) => {
if let Some(val) = self.constants.get(&s.to_id()) {
res.insert(s.sym.to_string(), val.clone()?);
} else {
return Err(s.span);
}
}
_ => return Err(obj.span),
},
PropOrSpread::Spread(spread) => {
let v = self.eval(&*spread.expr)?;
match v {
JsValue::Object(o) => res.extend(o),
_ => return Err(obj.span),
}
}
}
}
Ok(JsValue::Object(res))
}

pub fn eval_member_prop(&self, obj: JsValue, member: &MemberExpr) -> Result<JsValue, Span> {
match &member.prop {
MemberProp::Ident(id) => obj.get_id(id.as_ref()).ok_or(member.span),
MemberProp::Computed(prop) => {
Expand All @@ -688,7 +704,7 @@ impl<'a> Macros<'a> {
}

/// Convert JS value to AST.
fn value_to_expr(&self, value: JsValue) -> Result<Expr, MacroError> {
pub fn value_to_expr(&self, value: JsValue) -> Result<Expr, MacroError> {
Ok(match value {
JsValue::Null => Expr::Lit(Lit::Null(Null::dummy())),
JsValue::Undefined => Expr::Ident(Ident::new_no_ctxt(js_word!("undefined"), DUMMY_SP)),
Expand Down Expand Up @@ -764,7 +780,7 @@ impl<'a> Macros<'a> {
})
}

fn eval_pat(&mut self, value: Result<JsValue, Span>, pat: &Pat) {
pub fn eval_pat(&mut self, value: Result<JsValue, Span>, pat: &Pat) {
match pat {
Pat::Ident(name) => {
self.constants.insert(name.to_id(), value);
Expand Down
2 changes: 1 addition & 1 deletion crates/node-bindings/src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub struct JsFileSystemOptions {
pub struct JsResolverOptions {
pub fs: Option<JsFileSystemOptions>,
pub include_node_modules: Option<NapiSideEffectsVariants>,
pub conditions: Option<u16>,
pub conditions: Option<u32>,
pub module_dir_resolver: Option<JsFunction>,
pub mode: u8,
pub entries: Option<u8>,
Expand Down
Loading
Loading