Skip to content

Commit

Permalink
DX improvements for "use cache" functions (#71518)
Browse files Browse the repository at this point in the history
With this PR, we're assigning names to the transformed `"use cache"`
functions, using the same rules as in #71478. Since we can't use named
function expressions here, we're using `Object.defineProperty` instead,
which we already used in #71478 for server actions with default export
expressions.

In addition, this PR fixes source mapping of `"use cache"` functions,
which was slightly off before, due to the wrong spans being assigned to
the new expressions.

**Before:**

<img width="943" alt="before"
src="https://github.com/user-attachments/assets/f7cf674c-32d4-4f0d-a304-d5de5637a6a1">

**After:**

<img width="943" alt="after"
src="https://github.com/user-attachments/assets/8edda482-a666-4431-bfad-7e21987331eb">
  • Loading branch information
unstubbable authored Oct 21, 2024
1 parent dfc5331 commit f2ed75a
Show file tree
Hide file tree
Showing 17 changed files with 222 additions and 70 deletions.
65 changes: 40 additions & 25 deletions crates/next-custom-transforms/src/transforms/server_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ impl<C: Comments> ServerActions<C> {
);

let register_action_expr = bind_args_to_ref_expr(
annotate_ident_as_server_reference(action_ident.clone(), action_id.clone()),
annotate_ident_as_server_reference(action_ident.clone(), action_id.clone(), arrow.span),
ids_from_closure
.iter()
.cloned()
Expand Down Expand Up @@ -377,7 +377,11 @@ impl<C: Comments> ServerActions<C> {
let action_id = generate_action_id(&self.config.hash_salt, &self.file_name, &action_name);

let register_action_expr = bind_args_to_ref_expr(
annotate_ident_as_server_reference(action_ident.clone(), action_id.clone()),
annotate_ident_as_server_reference(
action_ident.clone(),
action_id.clone(),
function.span,
),
ids_from_closure
.iter()
.cloned()
Expand Down Expand Up @@ -652,14 +656,21 @@ impl<C: Comments> ServerActions<C> {
.into(),
})));

if let Some(Ident { sym, .. }) = &self.arrow_or_fn_expr_ident {
assign_name_to_ident(&cache_ident, sym.as_str(), &mut self.hoisted_extra_items);
}

let bound_args: Vec<_> = ids_from_closure
.iter()
.cloned()
.map(|id| Some(id.as_arg()))
.collect();

let register_action_expr =
annotate_ident_as_server_reference(cache_ident.clone(), reference_id.clone());
let register_action_expr = annotate_ident_as_server_reference(
cache_ident.clone(),
reference_id.clone(),
arrow.span,
);

// If there're any bound args from the closure, we need to hoist the
// register action expression to the top-level, and return the bind
Expand Down Expand Up @@ -709,8 +720,11 @@ impl<C: Comments> ServerActions<C> {

let reference_id = generate_action_id(&self.config.hash_salt, &self.file_name, &cache_name);

let register_action_expr =
annotate_ident_as_server_reference(cache_ident.clone(), reference_id.clone());
let register_action_expr = annotate_ident_as_server_reference(
cache_ident.clone(),
reference_id.clone(),
function.span,
);

function.body.visit_mut_with(&mut ClosureReplacer {
used_ids: &ids_from_closure,
Expand Down Expand Up @@ -805,7 +819,7 @@ impl<C: Comments> ServerActions<C> {
name: Pat::Ident(cache_ident.clone().into()),
init: Some(wrap_cache_expr(
Box::new(Expr::Fn(FnExpr {
ident: fn_name,
ident: fn_name.clone(),
function: Box::new(Function {
params: new_params,
body: new_body,
Expand All @@ -822,6 +836,12 @@ impl<C: Comments> ServerActions<C> {
.into(),
})));

if let Some(Ident { sym, .. }) = fn_name {
assign_name_to_ident(&cache_ident, sym.as_str(), &mut self.hoisted_extra_items);
} else if self.in_default_export_decl {
assign_name_to_ident(&cache_ident, "default", &mut self.hoisted_extra_items);
}

let bound_args: Vec<_> = ids_from_closure
.iter()
.cloned()
Expand Down Expand Up @@ -947,16 +967,6 @@ impl<C: Comments> VisitMut for ServerActions<C> {
}

if let Some(cache_type_str) = cache_type {
// It's a cache function. If it doesn't have a name, give it one.
match f.ident.as_mut() {
None => {
let action_name = gen_cache_ident(&mut self.reference_index);
let ident = Ident::new(action_name, DUMMY_SP, Default::default());
f.ident.insert(ident)
}
Some(i) => i,
};

// Collect all the identifiers defined inside the closure and used
// in the cache function. With deduplication.
retain_names_from_declared_idents(
Expand All @@ -966,7 +976,7 @@ impl<C: Comments> VisitMut for ServerActions<C> {

let new_expr = self.maybe_hoist_and_create_proxy_for_cache_function(
child_names.clone(),
f.ident.clone(),
f.ident.clone().or(self.arrow_or_fn_expr_ident.clone()),
cache_type_str.as_str(),
&mut f.function,
);
Expand Down Expand Up @@ -1489,7 +1499,7 @@ impl<C: Comments> VisitMut for ServerActions<C> {
self.exported_idents
.push((new_ident.clone(), "default".into()));

attach_name_to_default_expr(&new_ident, &mut self.extra_items);
assign_name_to_ident(&new_ident, "default", &mut self.extra_items);
}
}
_ => {
Expand All @@ -1514,7 +1524,7 @@ impl<C: Comments> VisitMut for ServerActions<C> {
.push((new_ident.clone(), "default".into()));

create_var_declarator(&new_ident, &mut self.extra_items);
attach_name_to_default_expr(&new_ident, &mut self.extra_items);
assign_name_to_ident(&new_ident, "default", &mut self.extra_items);

*default_expr.expr =
assign_arrow_expr(&new_ident, Expr::Arrow(arrow.clone()));
Expand All @@ -1538,7 +1548,7 @@ impl<C: Comments> VisitMut for ServerActions<C> {
.push((new_ident.clone(), "default".into()));

create_var_declarator(&new_ident, &mut self.extra_items);
attach_name_to_default_expr(&new_ident, &mut self.extra_items);
assign_name_to_ident(&new_ident, "default", &mut self.extra_items);

*default_expr.expr =
assign_arrow_expr(&new_ident, Expr::Call(call.clone()));
Expand Down Expand Up @@ -1726,6 +1736,7 @@ impl<C: Comments> VisitMut for ServerActions<C> {
expr: Box::new(annotate_ident_as_server_reference(
ident.clone(),
action_id,
ident.span,
)),
}));
}
Expand Down Expand Up @@ -2079,7 +2090,7 @@ fn create_var_declarator(ident: &Ident, extra_items: &mut Vec<ModuleItem>) {
})))));
}

fn attach_name_to_default_expr(ident: &Ident, extra_items: &mut Vec<ModuleItem>) {
fn assign_name_to_ident(ident: &Ident, name: &str, extra_items: &mut Vec<ModuleItem>) {
// Assign a name with `Object.defineProperty($$ACTION_0, 'name', {value: 'default'})`
extra_items.push(ModuleItem::Stmt(Stmt::Expr(ExprStmt {
span: DUMMY_SP,
Expand Down Expand Up @@ -2110,7 +2121,7 @@ fn attach_name_to_default_expr(ident: &Ident, extra_items: &mut Vec<ModuleItem>)
props: vec![
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Str("value".into()),
value: Box::new("default".into()),
value: Box::new(name.into()),
}))),
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Str("writable".into()),
Expand Down Expand Up @@ -2155,10 +2166,14 @@ fn generate_action_id(hash_salt: &str, file_name: &str, export_name: &str) -> St
hex_encode(result)
}

fn annotate_ident_as_server_reference(ident: Ident, action_id: String) -> Expr {
fn annotate_ident_as_server_reference(
ident: Ident,
action_id: String,
original_span: Span,
) -> Expr {
// registerServerReference(reference, id, null)
Expr::Call(CallExpr {
span: ident.span,
span: original_span,
callee: quote_ident!("registerServerReference").as_callee(),
args: vec![
ExprOrSpread {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ const v = 'world';
export var $$RSC_SERVER_CACHE_0 = $$cache__("default", "3128060c414d59f8552e4788b846c0d2b7f74743", async function fn() {
return 'hello, ' + v;
});
Object.defineProperty($$RSC_SERVER_CACHE_0, "name", {
"value": "fn",
"writable": false
});
var fn = registerServerReference($$RSC_SERVER_CACHE_0, "3128060c414d59f8552e4788b846c0d2b7f74743", null);
export async function Component() {
const data = await fn();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,42 @@
/* __next_internal_action_entry_do_not_use__ {"12a8d21b6362b4cc8f5b15560525095bc48dba80":"$$RSC_SERVER_CACHE_3","3128060c414d59f8552e4788b846c0d2b7f74743":"$$RSC_SERVER_CACHE_0","4acc55633206134002149ce873fe498be64a6fef":"$$RSC_SERVER_CACHE_4","951c375b4a6a6e89d67b743ec5808127cfde405d":"$$RSC_SERVER_CACHE_1"} */ import { registerServerReference } from "private-next-rsc-server-reference";
/* __next_internal_action_entry_do_not_use__ {"12a8d21b6362b4cc8f5b15560525095bc48dba80":"$$RSC_SERVER_CACHE_3","3128060c414d59f8552e4788b846c0d2b7f74743":"$$RSC_SERVER_CACHE_0","69348c79fce073bae2f70f139565a2fda1c74c74":"$$RSC_SERVER_CACHE_2","951c375b4a6a6e89d67b743ec5808127cfde405d":"$$RSC_SERVER_CACHE_1"} */ import { registerServerReference } from "private-next-rsc-server-reference";
import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption";
import { cache as $$cache__ } from "private-next-rsc-cache-wrapper";
export var $$RSC_SERVER_CACHE_0 = $$cache__("default", "3128060c414d59f8552e4788b846c0d2b7f74743", async function() {
return 'foo';
});
Object.defineProperty($$RSC_SERVER_CACHE_0, "name", {
"value": "foo",
"writable": false
});
const foo = registerServerReference($$RSC_SERVER_CACHE_0, "3128060c414d59f8552e4788b846c0d2b7f74743", null);
export { bar };
export var $$RSC_SERVER_CACHE_1 = $$cache__("default", "951c375b4a6a6e89d67b743ec5808127cfde405d", async function bar() {
return 'bar';
});
Object.defineProperty($$RSC_SERVER_CACHE_1, "name", {
"value": "bar",
"writable": false
});
var bar = registerServerReference($$RSC_SERVER_CACHE_1, "951c375b4a6a6e89d67b743ec5808127cfde405d", null);
// Should not be wrapped in $$cache__.
const qux = async function qux() {
return 'qux';
};
export var $$RSC_SERVER_CACHE_3 = $$cache__("default", "12a8d21b6362b4cc8f5b15560525095bc48dba80", async function $$RSC_SERVER_CACHE_2() {
export var $$RSC_SERVER_CACHE_2 = $$cache__("default", "69348c79fce073bae2f70f139565a2fda1c74c74", async function baz() {
return qux() + 'baz';
});
const baz = registerServerReference($$RSC_SERVER_CACHE_3, "12a8d21b6362b4cc8f5b15560525095bc48dba80", null);
export var $$RSC_SERVER_CACHE_4 = $$cache__("default", "4acc55633206134002149ce873fe498be64a6fef", async function() {
Object.defineProperty($$RSC_SERVER_CACHE_2, "name", {
"value": "baz",
"writable": false
});
const baz = registerServerReference($$RSC_SERVER_CACHE_2, "69348c79fce073bae2f70f139565a2fda1c74c74", null);
export var $$RSC_SERVER_CACHE_3 = $$cache__("default", "12a8d21b6362b4cc8f5b15560525095bc48dba80", async function() {
return 'quux';
});
const quux = registerServerReference($$RSC_SERVER_CACHE_4, "4acc55633206134002149ce873fe498be64a6fef", null);
Object.defineProperty($$RSC_SERVER_CACHE_3, "name", {
"value": "quux",
"writable": false
});
const quux = registerServerReference($$RSC_SERVER_CACHE_3, "12a8d21b6362b4cc8f5b15560525095bc48dba80", null);
export { foo, baz };
export default quux;
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ import { cache as $$cache__ } from "private-next-rsc-cache-wrapper";
export var $$RSC_SERVER_CACHE_0 = $$cache__("default", "3128060c414d59f8552e4788b846c0d2b7f74743", async function() {
return 'data';
});
Object.defineProperty($$RSC_SERVER_CACHE_0, "name", {
"value": "my_fn",
"writable": false
});
export const my_fn = registerServerReference($$RSC_SERVER_CACHE_0, "3128060c414d59f8552e4788b846c0d2b7f74743", null);
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
/* __next_internal_action_entry_do_not_use__ {"3128060c414d59f8552e4788b846c0d2b7f74743":"$$RSC_SERVER_CACHE_0","4acc55633206134002149ce873fe498be64a6fef":"$$RSC_SERVER_CACHE_4","69348c79fce073bae2f70f139565a2fda1c74c74":"$$RSC_SERVER_CACHE_2","951c375b4a6a6e89d67b743ec5808127cfde405d":"$$RSC_SERVER_CACHE_1"} */ import { registerServerReference } from "private-next-rsc-server-reference";
/* __next_internal_action_entry_do_not_use__ {"12a8d21b6362b4cc8f5b15560525095bc48dba80":"$$RSC_SERVER_CACHE_3","3128060c414d59f8552e4788b846c0d2b7f74743":"$$RSC_SERVER_CACHE_0","69348c79fce073bae2f70f139565a2fda1c74c74":"$$RSC_SERVER_CACHE_2","951c375b4a6a6e89d67b743ec5808127cfde405d":"$$RSC_SERVER_CACHE_1"} */ import { registerServerReference } from "private-next-rsc-server-reference";
import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption";
import { cache as $$cache__ } from "private-next-rsc-cache-wrapper";
export var $$RSC_SERVER_CACHE_0 = $$cache__("default", "3128060c414d59f8552e4788b846c0d2b7f74743", async function foo() {
return 'data A';
});
Object.defineProperty($$RSC_SERVER_CACHE_0, "name", {
"value": "foo",
"writable": false
});
export var foo = registerServerReference($$RSC_SERVER_CACHE_0, "3128060c414d59f8552e4788b846c0d2b7f74743", null);
export var $$RSC_SERVER_CACHE_1 = $$cache__("default", "951c375b4a6a6e89d67b743ec5808127cfde405d", async function bar() {
return 'data B';
});
Object.defineProperty($$RSC_SERVER_CACHE_1, "name", {
"value": "bar",
"writable": false
});
export var bar = registerServerReference($$RSC_SERVER_CACHE_1, "951c375b4a6a6e89d67b743ec5808127cfde405d", null);
export var $$RSC_SERVER_CACHE_2 = $$cache__("default", "69348c79fce073bae2f70f139565a2fda1c74c74", async function Cached({ children }) {
return children;
});
Object.defineProperty($$RSC_SERVER_CACHE_2, "name", {
"value": "Cached",
"writable": false
});
export default registerServerReference($$RSC_SERVER_CACHE_2, "69348c79fce073bae2f70f139565a2fda1c74c74", null);
export var $$RSC_SERVER_CACHE_4 = $$cache__("default", "4acc55633206134002149ce873fe498be64a6fef", async function $$RSC_SERVER_CACHE_3() {
export var $$RSC_SERVER_CACHE_3 = $$cache__("default", "12a8d21b6362b4cc8f5b15560525095bc48dba80", async function baz() {
return 'data C';
});
export const baz = registerServerReference($$RSC_SERVER_CACHE_4, "4acc55633206134002149ce873fe498be64a6fef", null);
Object.defineProperty($$RSC_SERVER_CACHE_3, "name", {
"value": "baz",
"writable": false
});
export const baz = registerServerReference($$RSC_SERVER_CACHE_3, "12a8d21b6362b4cc8f5b15560525095bc48dba80", null);
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { cache as $$cache__ } from "private-next-rsc-cache-wrapper";
export var $$RSC_SERVER_CACHE_0 = $$cache__("default", "3128060c414d59f8552e4788b846c0d2b7f74743", async function fn() {
return 'foo';
});
Object.defineProperty($$RSC_SERVER_CACHE_0, "name", {
"value": "fn",
"writable": false
});
var fn = registerServerReference($$RSC_SERVER_CACHE_0, "3128060c414d59f8552e4788b846c0d2b7f74743", null);
async function Component() {
const data = await fn();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ import { cache as $$cache__ } from "private-next-rsc-cache-wrapper";
export var $$RSC_SERVER_CACHE_0 = $$cache__("x", "3128060c414d59f8552e4788b846c0d2b7f74743", async function foo() {
return 'data';
});
Object.defineProperty($$RSC_SERVER_CACHE_0, "name", {
"value": "foo",
"writable": false
});
export var foo = registerServerReference($$RSC_SERVER_CACHE_0, "3128060c414d59f8552e4788b846c0d2b7f74743", null);
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export var $$RSC_SERVER_CACHE_0 = $$cache__("default", "3128060c414d59f8552e4788
foo: $$ACTION_ARG_1
};
});
Object.defineProperty($$RSC_SERVER_CACHE_0, "name", {
"value": "fn",
"writable": false
});
async function Component({ foo }) {
const a = 123;
var fn = $$RSC_SERVER_REF_1.bind(null, encryptActionBoundArgs("3128060c414d59f8552e4788b846c0d2b7f74743", [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export var $$RSC_SERVER_CACHE_0 = $$cache__("default", "3128060c414d59f8552e4788
}
];
});
Object.defineProperty($$RSC_SERVER_CACHE_0, "name", {
"value": "cache",
"writable": false
});
export const $$RSC_SERVER_ACTION_2 = async function action($$ACTION_CLOSURE_BOUND, c) {
var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("1c36b06e398c97abe5d5d7ae8c672bfddf4e1b91", $$ACTION_CLOSURE_BOUND);
const d = $$ACTION_ARG_0 + $$ACTION_ARG_1 + c;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ export var $$RSC_SERVER_CACHE_1 = $$cache__("default", "951c375b4a6a6e89d67b743e
const data = await fn();
return <div>{data}</div>;
});
Object.defineProperty($$RSC_SERVER_CACHE_1, "name", {
"value": "Component",
"writable": false
});
export var Component = registerServerReference($$RSC_SERVER_CACHE_1, "951c375b4a6a6e89d67b743ec5808127cfde405d", null);
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ export var $$RSC_SERVER_CACHE_1 = $$cache__("default", "951c375b4a6a6e89d67b743e
r: children
};
});
Object.defineProperty($$RSC_SERVER_CACHE_1, "name", {
"value": "getCachedRandom",
"writable": false
});
var getCachedRandom = registerServerReference($$RSC_SERVER_CACHE_1, "951c375b4a6a6e89d67b743ec5808127cfde405d", null);
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ function Foo() {
export var $$RSC_SERVER_CACHE_0 = $$cache__("default", "3128060c414d59f8552e4788b846c0d2b7f74743", async function bar() {
return <Foo/>;
});
Object.defineProperty($$RSC_SERVER_CACHE_0, "name", {
"value": "bar",
"writable": false
});
export var bar = registerServerReference($$RSC_SERVER_CACHE_0, "3128060c414d59f8552e4788b846c0d2b7f74743", null);
39 changes: 39 additions & 0 deletions test/e2e/app-dir/use-cache/app/form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use client'

import { useActionState } from 'react'

export function Form({
foo,
bar,
baz,
}: {
foo: () => Promise<number>
bar: () => Promise<number>
baz: () => Promise<number>
}) {
const [result, dispatch] = useActionState<
[number, number, number],
'submit' | 'reset'
>(
async (_state, event) => {
if (event === 'reset') {
return [0, 0, 0]
}

return [await foo(), await bar(), await baz()]
},
[0, 0, 0]
)

return (
<form action={() => dispatch('submit')}>
<button id="submit-button">Submit</button>{' '}
<button id="reset-button" formAction={() => dispatch('reset')}>
Reset
</button>
<p>
{result[0]} {result[1]} {result[2]}
</p>
</form>
)
}
Loading

0 comments on commit f2ed75a

Please sign in to comment.