Fix shorthand prop wrongly handled in the Server Actions compiler (#50937)
This PR fixes the case of having a shorthand prop `{ id }` referenced from a non-top level closure in the Server Actions compiler. The main problem is that an `Ident` in `PropOrSpread` is not considered as an `Expr`. See corresponding issue report and test case for more details. fix #50445 fix NEXT-1254
This commit is contained in:
parent
e562ddf990
commit
18d2f7da33
3 changed files with 52 additions and 5 deletions
|
@ -357,7 +357,7 @@ impl<C: Comments> ServerActions<C> {
|
|||
// export async function $ACTION_myAction () {}
|
||||
let mut new_params: Vec<Param> = vec![];
|
||||
|
||||
// $$ACTION_ARG_{index}
|
||||
// add params from closure collected ids
|
||||
for i in 0..ids_from_closure.len() {
|
||||
new_params.push(Param {
|
||||
span: DUMMY_SP,
|
||||
|
@ -618,6 +618,20 @@ impl<C: Comments> VisitMut for ServerActions<C> {
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_mut_prop_or_spread(&mut self, n: &mut PropOrSpread) {
|
||||
if self.in_action_fn && self.in_action_closure {
|
||||
if let PropOrSpread::Prop(box Prop::Shorthand(i)) = n {
|
||||
self.in_action_closure = false;
|
||||
self.action_closure_idents.push(Name::from(&*i));
|
||||
n.visit_mut_children_with(self);
|
||||
self.in_action_closure = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
n.visit_mut_children_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_expr(&mut self, n: &mut Expr) {
|
||||
if self.in_action_fn && self.in_action_closure {
|
||||
if let Ok(name) = Name::try_from(&*n) {
|
||||
|
@ -1474,12 +1488,37 @@ impl VisitMut for ClosureReplacer<'_> {
|
|||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_prop_or_spread(&mut self, n: &mut PropOrSpread) {
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
if let PropOrSpread::Prop(box Prop::Shorthand(i)) = n {
|
||||
let name = Name::from(&*i);
|
||||
if let Some(index) = self.used_ids.iter().position(|used_id| *used_id == name) {
|
||||
*n = PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
|
||||
key: PropName::Ident(i.clone()),
|
||||
value: Box::new(Expr::Ident(Ident::new(
|
||||
// $$ACTION_ARG_0
|
||||
format!("$$ACTION_ARG_{}", index).into(),
|
||||
DUMMY_SP,
|
||||
))),
|
||||
})));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
noop_visit_mut_type!();
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct Name(Id, Vec<(JsWord, bool)>);
|
||||
|
||||
impl From<&'_ Ident> for Name {
|
||||
fn from(value: &Ident) -> Self {
|
||||
Name(value.to_id(), vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&'_ Expr> for Name {
|
||||
type Error = ();
|
||||
|
||||
|
|
|
@ -2,13 +2,15 @@ import deleteFromDb from 'db'
|
|||
|
||||
const v1 = 'v1'
|
||||
|
||||
export function Item({ id1, id2 }) {
|
||||
export function Item({ id1, id2, id3, id4 }) {
|
||||
const v2 = id2
|
||||
async function deleteItem() {
|
||||
'use server'
|
||||
await deleteFromDb(id1)
|
||||
await deleteFromDb(v1)
|
||||
await deleteFromDb(v2)
|
||||
await deleteFromDb({ id3 })
|
||||
await deleteFromDb(id4.x)
|
||||
}
|
||||
return <Button action={deleteItem}>Delete</Button>
|
||||
}
|
||||
|
|
|
@ -1,19 +1,25 @@
|
|||
/* __next_internal_action_entry_do_not_use__ $$ACTION_0 */ import __create_action_proxy__ from "private-next-rsc-action-proxy";
|
||||
import deleteFromDb from 'db';
|
||||
const v1 = 'v1';
|
||||
export function Item({ id1 , id2 }) {
|
||||
export function Item({ id1 , id2 , id3 , id4 }) {
|
||||
const v2 = id2;
|
||||
async function deleteItem(...args) {
|
||||
return $$ACTION_0.apply(null, (deleteItem.$$bound || []).concat(args));
|
||||
}
|
||||
__create_action_proxy__("6d53ce510b2e36499b8f56038817b9bad86cabb4", [
|
||||
id1,
|
||||
v2
|
||||
v2,
|
||||
id3,
|
||||
id4.x
|
||||
], deleteItem, $$ACTION_0);
|
||||
return <Button action={deleteItem}>Delete</Button>;
|
||||
}
|
||||
export async function $$ACTION_0($$ACTION_ARG_0, $$ACTION_ARG_1) {
|
||||
export async function $$ACTION_0($$ACTION_ARG_0, $$ACTION_ARG_1, $$ACTION_ARG_2, $$ACTION_ARG_3) {
|
||||
await deleteFromDb($$ACTION_ARG_0);
|
||||
await deleteFromDb(v1);
|
||||
await deleteFromDb($$ACTION_ARG_1);
|
||||
await deleteFromDb({
|
||||
id3: $$ACTION_ARG_2
|
||||
});
|
||||
await deleteFromDb($$ACTION_ARG_3);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue