Support HOC cases in server entries (#47379)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
parent
6a6977cb71
commit
e6a3bab489
11 changed files with 366 additions and 62 deletions
|
@ -47,6 +47,7 @@ pub fn server_actions<C: Comments>(
|
||||||
closure_idents: Default::default(),
|
closure_idents: Default::default(),
|
||||||
action_idents: Default::default(),
|
action_idents: Default::default(),
|
||||||
exported_idents: Default::default(),
|
exported_idents: Default::default(),
|
||||||
|
inlined_action_idents: Default::default(),
|
||||||
|
|
||||||
annotations: Default::default(),
|
annotations: Default::default(),
|
||||||
extra_items: Default::default(),
|
extra_items: Default::default(),
|
||||||
|
@ -73,6 +74,7 @@ struct ServerActions<C: Comments> {
|
||||||
in_action_closure: bool,
|
in_action_closure: bool,
|
||||||
closure_idents: Vec<Id>,
|
closure_idents: Vec<Id>,
|
||||||
action_idents: Vec<Name>,
|
action_idents: Vec<Name>,
|
||||||
|
inlined_action_idents: Vec<(Id, Id)>,
|
||||||
|
|
||||||
// (ident, export name)
|
// (ident, export name)
|
||||||
exported_idents: Vec<(Id, String)>,
|
exported_idents: Vec<(Id, String)>,
|
||||||
|
@ -125,11 +127,17 @@ impl<C: Comments> ServerActions<C> {
|
||||||
ident: &Ident,
|
ident: &Ident,
|
||||||
function: Option<&mut Box<Function>>,
|
function: Option<&mut Box<Function>>,
|
||||||
arrow: Option<&mut ArrowExpr>,
|
arrow: Option<&mut ArrowExpr>,
|
||||||
|
call_expr_and_ident: Option<(&mut CallExpr, CallExpr, Ident)>,
|
||||||
return_paren: bool,
|
return_paren: bool,
|
||||||
) -> (Option<Box<ParenExpr>>, Option<Box<Function>>) {
|
) -> (Option<Box<ParenExpr>>, Option<Box<Function>>) {
|
||||||
let action_name: JsWord = gen_ident(&mut self.ident_cnt);
|
let action_name: JsWord = gen_ident(&mut self.ident_cnt);
|
||||||
let action_ident = private_ident!(action_name.clone());
|
let action_ident = private_ident!(action_name.clone());
|
||||||
|
|
||||||
|
if !self.in_action_file {
|
||||||
|
self.inlined_action_idents
|
||||||
|
.push((ident.to_id(), action_ident.to_id()));
|
||||||
|
}
|
||||||
|
|
||||||
let export_name: JsWord = if self.in_default_export_decl {
|
let export_name: JsWord = if self.in_default_export_decl {
|
||||||
"default".into()
|
"default".into()
|
||||||
} else {
|
} else {
|
||||||
|
@ -140,7 +148,7 @@ impl<C: Comments> ServerActions<C> {
|
||||||
self.export_actions.push(export_name.to_string());
|
self.export_actions.push(export_name.to_string());
|
||||||
|
|
||||||
// If it's already a top level function, we don't need to hoist it.
|
// If it's already a top level function, we don't need to hoist it.
|
||||||
if self.top_level && arrow.is_none() {
|
if self.top_level && arrow.is_none() && call_expr_and_ident.is_none() {
|
||||||
annotate_ident_as_action(
|
annotate_ident_as_action(
|
||||||
&mut self.annotations,
|
&mut self.annotations,
|
||||||
ident.clone(),
|
ident.clone(),
|
||||||
|
@ -148,6 +156,7 @@ impl<C: Comments> ServerActions<C> {
|
||||||
self.file_name.to_string(),
|
self.file_name.to_string(),
|
||||||
export_name.to_string(),
|
export_name.to_string(),
|
||||||
false,
|
false,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
// export const $ACTION_myAction = myAction;
|
// export const $ACTION_myAction = myAction;
|
||||||
|
@ -205,6 +214,7 @@ impl<C: Comments> ServerActions<C> {
|
||||||
self.file_name.to_string(),
|
self.file_name.to_string(),
|
||||||
export_name.to_string(),
|
export_name.to_string(),
|
||||||
true,
|
true,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let BlockStmtOrExpr::BlockStmt(block) = &mut *a.body {
|
if let BlockStmtOrExpr::BlockStmt(block) = &mut *a.body {
|
||||||
|
@ -304,6 +314,7 @@ impl<C: Comments> ServerActions<C> {
|
||||||
self.file_name.to_string(),
|
self.file_name.to_string(),
|
||||||
export_name.to_string(),
|
export_name.to_string(),
|
||||||
true,
|
true,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
f.body.visit_mut_with(&mut ClosureReplacer {
|
f.body.visit_mut_with(&mut ClosureReplacer {
|
||||||
|
@ -391,6 +402,60 @@ impl<C: Comments> ServerActions<C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (None, Some(Box::new(new_fn)));
|
return (None, Some(Box::new(new_fn)));
|
||||||
|
} else if let Some((c, original_call, inner_action_ident)) = call_expr_and_ident {
|
||||||
|
let mut arrow_annotations = Vec::new();
|
||||||
|
annotate_ident_as_action(
|
||||||
|
&mut arrow_annotations,
|
||||||
|
ident.clone(),
|
||||||
|
vec![],
|
||||||
|
self.file_name.to_string(),
|
||||||
|
export_name.to_string(),
|
||||||
|
true,
|
||||||
|
Some(inner_action_ident),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.extra_items
|
||||||
|
.push(ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
decl: Decl::Var(Box::new(VarDecl {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
kind: VarDeclKind::Const,
|
||||||
|
declare: Default::default(),
|
||||||
|
decls: vec![VarDeclarator {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
name: action_ident.into(),
|
||||||
|
init: Some(Box::new(Expr::Call(c.clone()))),
|
||||||
|
definite: Default::default(),
|
||||||
|
}],
|
||||||
|
})),
|
||||||
|
})));
|
||||||
|
|
||||||
|
// Create a paren expr to wrap all annotations:
|
||||||
|
// ($ACTION = hoc(...), $ACTION.$$id = "..", .., $ACTION)
|
||||||
|
let mut exprs = vec![Box::new(Expr::Assign(AssignExpr {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
left: PatOrExpr::Pat(Box::new(Pat::Ident(ident.clone().into()))),
|
||||||
|
op: op!("="),
|
||||||
|
right: Box::new(Expr::Call(original_call)),
|
||||||
|
}))];
|
||||||
|
exprs.extend(arrow_annotations.into_iter().map(|a| {
|
||||||
|
if let Stmt::Expr(ExprStmt { expr, .. }) = a {
|
||||||
|
expr
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
exprs.push(Box::new(Expr::Ident(ident.clone())));
|
||||||
|
|
||||||
|
let new_paren = ParenExpr {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
expr: Box::new(Expr::Seq(SeqExpr {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
exprs,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
return (Some(Box::new(new_paren)), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,6 +565,7 @@ impl<C: Comments> VisitMut for ServerActions<C> {
|
||||||
&f.ident,
|
&f.ident,
|
||||||
Some(&mut f.function),
|
Some(&mut f.function),
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -611,73 +677,143 @@ impl<C: Comments> VisitMut for ServerActions<C> {
|
||||||
|
|
||||||
n.visit_mut_children_with(self);
|
n.visit_mut_children_with(self);
|
||||||
|
|
||||||
if !self.in_action_file {
|
if self.in_action_file {
|
||||||
match n {
|
return;
|
||||||
Expr::Arrow(a) => {
|
}
|
||||||
let is_action_fn = self.get_action_info(
|
|
||||||
if let BlockStmtOrExpr::BlockStmt(block) = &mut *a.body {
|
|
||||||
Some(block)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
if is_action_fn {
|
match n {
|
||||||
// We need to give a name to the arrow function
|
Expr::Arrow(a) => {
|
||||||
// action and hoist it to the top.
|
let is_action_fn = self.get_action_info(
|
||||||
let action_name = gen_ident(&mut self.ident_cnt);
|
if let BlockStmtOrExpr::BlockStmt(block) = &mut *a.body {
|
||||||
let ident = private_ident!(action_name);
|
Some(block)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
let (maybe_new_paren, _) = self.add_action_annotations_and_maybe_hoist(
|
if !is_action_fn {
|
||||||
&ident,
|
return;
|
||||||
None,
|
|
||||||
Some(a),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
*n = attach_name_to_expr(
|
|
||||||
ident,
|
|
||||||
if let Some(new_paren) = maybe_new_paren {
|
|
||||||
Expr::Paren(*new_paren)
|
|
||||||
} else {
|
|
||||||
Expr::Arrow(a.clone())
|
|
||||||
},
|
|
||||||
&mut self.extra_items,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Expr::Fn(f) => {
|
|
||||||
let is_action_fn = self.get_action_info(f.function.body.as_mut(), true);
|
|
||||||
|
|
||||||
if is_action_fn {
|
// We need to give a name to the arrow function
|
||||||
let ident = match f.ident.as_mut() {
|
// action and hoist it to the top.
|
||||||
None => {
|
let action_name = gen_ident(&mut self.ident_cnt);
|
||||||
let action_name = gen_ident(&mut self.ident_cnt);
|
let ident = private_ident!(action_name);
|
||||||
let ident = Ident::new(action_name, DUMMY_SP);
|
|
||||||
f.ident.insert(ident)
|
|
||||||
}
|
|
||||||
Some(i) => i,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (maybe_new_paren, _) = self.add_action_annotations_and_maybe_hoist(
|
let (maybe_new_paren, _) =
|
||||||
ident,
|
self.add_action_annotations_and_maybe_hoist(&ident, None, Some(a), None, true);
|
||||||
Some(&mut f.function),
|
|
||||||
None,
|
*n = attach_name_to_expr(
|
||||||
true,
|
ident,
|
||||||
);
|
if let Some(new_paren) = maybe_new_paren {
|
||||||
|
Expr::Paren(*new_paren)
|
||||||
|
} else {
|
||||||
|
Expr::Arrow(a.clone())
|
||||||
|
},
|
||||||
|
&mut self.extra_items,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Expr::Fn(f) => {
|
||||||
|
let is_action_fn = self.get_action_info(f.function.body.as_mut(), true);
|
||||||
|
|
||||||
|
if !is_action_fn {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let ident = match f.ident.as_mut() {
|
||||||
|
None => {
|
||||||
|
let action_name = gen_ident(&mut self.ident_cnt);
|
||||||
|
let ident = Ident::new(action_name, DUMMY_SP);
|
||||||
|
f.ident.insert(ident)
|
||||||
|
}
|
||||||
|
Some(i) => i,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (maybe_new_paren, _) = self.add_action_annotations_and_maybe_hoist(
|
||||||
|
ident,
|
||||||
|
Some(&mut f.function),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(new_paren) = maybe_new_paren {
|
||||||
|
*n = attach_name_to_expr(
|
||||||
|
ident.clone(),
|
||||||
|
Expr::Paren(*new_paren),
|
||||||
|
&mut self.extra_items,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Call(c) => {
|
||||||
|
// Here we need to handle HOCs that wrap actions, e.g.:
|
||||||
|
// withValidator(($ACTION = async function () { ... }, ...))
|
||||||
|
|
||||||
|
// For now, we only handle the case where the HOC has a single argument:
|
||||||
|
// the action function.
|
||||||
|
if c.args.len() != 1 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ExprOrSpread {
|
||||||
|
expr:
|
||||||
|
box Expr::Paren(ParenExpr {
|
||||||
|
expr: box Expr::Seq(seq_expr),
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
..
|
||||||
|
}) = c.args.first_mut()
|
||||||
|
{
|
||||||
|
if let Some(box Expr::Assign(AssignExpr {
|
||||||
|
left: PatOrExpr::Pat(box Pat::Ident(pat_id)),
|
||||||
|
..
|
||||||
|
})) = seq_expr.exprs.first_mut()
|
||||||
|
{
|
||||||
|
let maybe_action_ident = self
|
||||||
|
.inlined_action_idents
|
||||||
|
.iter()
|
||||||
|
.find(|id| id.0 == pat_id.id.to_id());
|
||||||
|
if let Some(action_ident) = maybe_action_ident {
|
||||||
|
// This is a HOC that wraps an
|
||||||
|
// action.
|
||||||
|
// We need to give a name to the result
|
||||||
|
// action and hoist it to the top.
|
||||||
|
let action_name = gen_ident(&mut self.ident_cnt);
|
||||||
|
let ident = private_ident!(action_name);
|
||||||
|
|
||||||
|
let mut new_call = CallExpr {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
callee: c.callee.clone(),
|
||||||
|
args: vec![ExprOrSpread {
|
||||||
|
spread: None,
|
||||||
|
expr: Box::new(Expr::Ident(action_ident.1.clone().into())),
|
||||||
|
}],
|
||||||
|
type_args: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (maybe_new_paren, _) = self.add_action_annotations_and_maybe_hoist(
|
||||||
|
&ident,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some((&mut new_call, c.clone(), action_ident.0.clone().into())),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(new_paren) = maybe_new_paren {
|
|
||||||
*n = attach_name_to_expr(
|
*n = attach_name_to_expr(
|
||||||
ident.clone(),
|
ident,
|
||||||
Expr::Paren(*new_paren),
|
if let Some(new_paren) = maybe_new_paren {
|
||||||
|
// Keep the original $$bound value.
|
||||||
|
Expr::Paren(*new_paren)
|
||||||
|
} else {
|
||||||
|
Expr::Call(c.clone())
|
||||||
|
},
|
||||||
&mut self.extra_items,
|
&mut self.extra_items,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -720,6 +856,7 @@ impl<C: Comments> VisitMut for ServerActions<C> {
|
||||||
match &**init {
|
match &**init {
|
||||||
Expr::Fn(_f) => {}
|
Expr::Fn(_f) => {}
|
||||||
Expr::Arrow(_a) => {}
|
Expr::Arrow(_a) => {}
|
||||||
|
Expr::Call(_c) => {}
|
||||||
_ => {
|
_ => {
|
||||||
disallowed_export_span = *span;
|
disallowed_export_span = *span;
|
||||||
}
|
}
|
||||||
|
@ -813,6 +950,20 @@ impl<C: Comments> VisitMut for ServerActions<C> {
|
||||||
// export default foo
|
// export default foo
|
||||||
self.exported_idents.push((ident.to_id(), "default".into()));
|
self.exported_idents.push((ident.to_id(), "default".into()));
|
||||||
}
|
}
|
||||||
|
Expr::Call(call) => {
|
||||||
|
// export default fn()
|
||||||
|
let new_ident =
|
||||||
|
Ident::new(gen_ident(&mut self.ident_cnt), DUMMY_SP);
|
||||||
|
|
||||||
|
self.exported_idents
|
||||||
|
.push((new_ident.to_id(), "default".into()));
|
||||||
|
|
||||||
|
*default_expr.expr = attach_name_to_expr(
|
||||||
|
new_ident,
|
||||||
|
Expr::Call(call.clone()),
|
||||||
|
&mut self.extra_items,
|
||||||
|
);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
disallowed_export_span = default_expr.span;
|
disallowed_export_span = default_expr.span;
|
||||||
}
|
}
|
||||||
|
@ -857,6 +1008,7 @@ impl<C: Comments> VisitMut for ServerActions<C> {
|
||||||
self.file_name.to_string(),
|
self.file_name.to_string(),
|
||||||
export_name.to_string(),
|
export_name.to_string(),
|
||||||
false,
|
false,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
if !self.config.is_server {
|
if !self.config.is_server {
|
||||||
let params_ident = private_ident!("args");
|
let params_ident = private_ident!("args");
|
||||||
|
@ -1078,6 +1230,7 @@ fn annotate_ident_as_action(
|
||||||
file_name: String,
|
file_name: String,
|
||||||
export_name: String,
|
export_name: String,
|
||||||
has_bound: bool,
|
has_bound: bool,
|
||||||
|
re_annotate_action: Option<Ident>,
|
||||||
) {
|
) {
|
||||||
// myAction.$$typeof = Symbol.for('react.server.reference');
|
// myAction.$$typeof = Symbol.for('react.server.reference');
|
||||||
annotations.push(annotate(
|
annotations.push(annotate(
|
||||||
|
@ -1109,11 +1262,23 @@ fn annotate_ident_as_action(
|
||||||
annotations.push(annotate(
|
annotations.push(annotate(
|
||||||
&ident,
|
&ident,
|
||||||
"$$bound",
|
"$$bound",
|
||||||
ArrayLit {
|
if let Some(re_annotate_ident) = re_annotate_action {
|
||||||
span: DUMMY_SP,
|
Box::new(Expr::Member(MemberExpr {
|
||||||
elems: bound,
|
span: DUMMY_SP,
|
||||||
}
|
obj: Box::new(Expr::Ident(re_annotate_ident)),
|
||||||
.into(),
|
prop: MemberProp::Ident(Ident {
|
||||||
|
sym: "$$bound".into(),
|
||||||
|
span: DUMMY_SP,
|
||||||
|
optional: false,
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
ArrayLit {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
elems: bound,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
// If an action doesn't have any bound values, we add a special property
|
// If an action doesn't have any bound values, we add a special property
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { validator, another } from 'auth'
|
||||||
|
|
||||||
|
const x = 1
|
||||||
|
|
||||||
|
export default function Page () {
|
||||||
|
const y = 1
|
||||||
|
return <Foo action={validator(async function (z) {
|
||||||
|
'use server'
|
||||||
|
return x + y + z
|
||||||
|
})} />
|
||||||
|
}
|
||||||
|
|
||||||
|
validator(async () => {
|
||||||
|
'use server'
|
||||||
|
})
|
||||||
|
|
||||||
|
another(validator(async () => {
|
||||||
|
'use server'
|
||||||
|
}))
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* __next_internal_action_entry_do_not_use__ $$ACTION_1,$$ACTION_3,$$ACTION_5,$$ACTION_7,$$ACTION_9,$$ACTION_11,$$ACTION_13 */ import { validator, another } from 'auth';
|
||||||
|
const x = 1;
|
||||||
|
export default function Page() {
|
||||||
|
const y = 1;
|
||||||
|
return <Foo action={$$ACTION_2 = validator(($$ACTION_0 = async function(z) {
|
||||||
|
return $$ACTION_1($$ACTION_0.$$bound);
|
||||||
|
}, $$ACTION_0.$$typeof = Symbol.for("react.server.reference"), $$ACTION_0.$$id = "188d5d945750dc32e2c842b93c75a65763d4a922", $$ACTION_0.$$bound = [
|
||||||
|
y
|
||||||
|
], $$ACTION_0)), $$ACTION_2.$$typeof = Symbol.for("react.server.reference"), $$ACTION_2.$$id = "56a859f462d35a297c46a1bbd1e6a9058c104ab8", $$ACTION_2.$$bound = $$ACTION_0.$$bound, $$ACTION_2}/>;
|
||||||
|
}
|
||||||
|
export async function $$ACTION_1(closure, z = closure[1]) {
|
||||||
|
return x + closure[0] + z;
|
||||||
|
}
|
||||||
|
var $$ACTION_0;
|
||||||
|
export const $$ACTION_3 = validator($$ACTION_1);
|
||||||
|
var $$ACTION_2;
|
||||||
|
$$ACTION_6 = validator(($$ACTION_4 = async ()=>$$ACTION_5($$ACTION_4.$$bound), $$ACTION_4.$$typeof = Symbol.for("react.server.reference"), $$ACTION_4.$$id = "1383664d1dc2d9cfe33b88df3fa0eaffef8b99bc", $$ACTION_4.$$bound = [
|
||||||
|
y
|
||||||
|
], $$ACTION_4)), $$ACTION_6.$$typeof = Symbol.for("react.server.reference"), $$ACTION_6.$$id = "faf016739650cb4995340c9d9ab06ce1c9407fa0", $$ACTION_6.$$bound = $$ACTION_4.$$bound, $$ACTION_6;
|
||||||
|
export const $$ACTION_5 = async (closure)=>{};
|
||||||
|
var $$ACTION_4;
|
||||||
|
export const $$ACTION_7 = validator($$ACTION_5);
|
||||||
|
var $$ACTION_6;
|
||||||
|
$$ACTION_12 = another(($$ACTION_10 = validator(($$ACTION_8 = async ()=>$$ACTION_9($$ACTION_8.$$bound), $$ACTION_8.$$typeof = Symbol.for("react.server.reference"), $$ACTION_8.$$id = "0d0ca9684921f1c6dc36a2ec55ce57ba31407820", $$ACTION_8.$$bound = [
|
||||||
|
y
|
||||||
|
], $$ACTION_8)), $$ACTION_10.$$typeof = Symbol.for("react.server.reference"), $$ACTION_10.$$id = "dd70487b74c2c510c55e3e68aa3614cfa780850d", $$ACTION_10.$$bound = $$ACTION_8.$$bound, $$ACTION_10)), $$ACTION_12.$$typeof = Symbol.for("react.server.reference"), $$ACTION_12.$$id = "57cbac1f8911efd298cb885cba89919b14153dc1", $$ACTION_12.$$bound = $$ACTION_10.$$bound, $$ACTION_12;
|
||||||
|
export const $$ACTION_9 = async (closure)=>{};
|
||||||
|
var $$ACTION_8;
|
||||||
|
export const $$ACTION_11 = validator($$ACTION_9);
|
||||||
|
var $$ACTION_10;
|
||||||
|
export const $$ACTION_13 = another($$ACTION_11);
|
||||||
|
var $$ACTION_12;
|
|
@ -0,0 +1,6 @@
|
||||||
|
'use server'
|
||||||
|
|
||||||
|
import { validator } from 'auth'
|
||||||
|
|
||||||
|
export const action = validator(async () => {})
|
||||||
|
export default validator(async () => {})
|
|
@ -0,0 +1,17 @@
|
||||||
|
/* __next_internal_action_entry_do_not_use__ action,default */ import { validator } from 'auth';
|
||||||
|
export const action = validator(async ()=>{});
|
||||||
|
export default $$ACTION_0 = validator(async ()=>{});
|
||||||
|
var $$ACTION_0;
|
||||||
|
import ensureServerEntryExports from "private-next-rsc-action-proxy";
|
||||||
|
ensureServerEntryExports([
|
||||||
|
action,
|
||||||
|
$$ACTION_0
|
||||||
|
]);
|
||||||
|
action.$$typeof = Symbol.for("react.server.reference");
|
||||||
|
action.$$id = "f14702b5a021dd117f7ec7a3c838f397c2046d3b";
|
||||||
|
action.$$bound = [];
|
||||||
|
action.$$with_bound = false;
|
||||||
|
$$ACTION_0.$$typeof = Symbol.for("react.server.reference");
|
||||||
|
$$ACTION_0.$$id = "c18c215a6b7cdc64bf709f3a714ffdef1bf9651d";
|
||||||
|
$$ACTION_0.$$bound = [];
|
||||||
|
$$ACTION_0.$$with_bound = false;
|
|
@ -14,5 +14,9 @@ export async function callServer(id: string, bound: any[]) {
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(await res.text())
|
||||||
|
}
|
||||||
|
|
||||||
return (await res.json())[0]
|
return (await res.json())[0]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1398,6 +1398,14 @@ export async function renderToHTMLOrFlight(
|
||||||
res.statusCode = 404
|
res.statusCode = 404
|
||||||
return new RenderResult(await bodyResult({ asNotFound: true }))
|
return new RenderResult(await bodyResult({ asNotFound: true }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isFetchAction) {
|
||||||
|
res.statusCode = 500
|
||||||
|
return new RenderResult(
|
||||||
|
(err as Error)?.message ?? 'Internal Server Error'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -65,6 +65,25 @@ createNextDescribe(
|
||||||
}, 'my-not-found')
|
}, 'my-not-found')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should support hoc auth wrappers', async () => {
|
||||||
|
const browser = await next.browser('/header')
|
||||||
|
await await browser.eval(`document.cookie = 'auth=0'`)
|
||||||
|
|
||||||
|
await browser.elementByCss('#authed').click()
|
||||||
|
|
||||||
|
await check(() => {
|
||||||
|
return browser.elementByCss('h1').text()
|
||||||
|
}, 'Error: Unauthorized request')
|
||||||
|
|
||||||
|
await await browser.eval(`document.cookie = 'auth=1'`)
|
||||||
|
|
||||||
|
await browser.elementByCss('#authed').click()
|
||||||
|
|
||||||
|
await check(() => {
|
||||||
|
return browser.elementByCss('h1').text()
|
||||||
|
}, 'HELLO, WORLD')
|
||||||
|
})
|
||||||
|
|
||||||
it('should support importing actions in client components', async () => {
|
it('should support importing actions in client components', async () => {
|
||||||
const browser = await next.browser('/client')
|
const browser = await next.browser('/client')
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,17 @@
|
||||||
import UI from './ui'
|
import UI from './ui'
|
||||||
|
|
||||||
import { getCookie, getHeader } from './actions'
|
import { getCookie, getHeader } from './actions'
|
||||||
|
import { validator } from './validator'
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return <UI getCookie={getCookie} getHeader={getHeader} />
|
return (
|
||||||
|
<UI
|
||||||
|
getCookie={getCookie}
|
||||||
|
getHeader={getHeader}
|
||||||
|
getAuthedUppercase={validator(async (str) => {
|
||||||
|
'use server'
|
||||||
|
return str.toUpperCase()
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
export default function UI({ getCookie, getHeader }) {
|
export default function UI({ getCookie, getHeader, getAuthedUppercase }) {
|
||||||
const [result, setResult] = useState('')
|
const [result, setResult] = useState('')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -29,6 +29,19 @@ export default function UI({ getCookie, getHeader }) {
|
||||||
>
|
>
|
||||||
getHeader
|
getHeader
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
id="authed"
|
||||||
|
onClick={async () => {
|
||||||
|
try {
|
||||||
|
const res = await getAuthedUppercase('hello, world')
|
||||||
|
setResult(res)
|
||||||
|
} catch (err) {
|
||||||
|
setResult('Error: ' + err.message)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
getAuthedUppercase
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
11
test/e2e/app-dir/actions/app/header/validator.js
Normal file
11
test/e2e/app-dir/actions/app/header/validator.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { cookies } from 'next/headers'
|
||||||
|
|
||||||
|
export function validator(action) {
|
||||||
|
return async function (...args) {
|
||||||
|
const auth = cookies().get('auth')
|
||||||
|
if (auth?.value !== '1') {
|
||||||
|
throw new Error('Unauthorized request')
|
||||||
|
}
|
||||||
|
return action(...args)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue