Skip to content

Commit

Permalink
Adjust transforms for cached functions imported from client components
Browse files Browse the repository at this point in the history
  • Loading branch information
unstubbable committed Oct 16, 2024
1 parent 0878db2 commit 4370913
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 17 deletions.
27 changes: 18 additions & 9 deletions crates/next-custom-transforms/src/transforms/server_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,13 @@ impl<C: Comments> ServerActions<C> {
}
}

if self.in_export_decl {
if self.in_action_file {
// All export functions in a server file are actions
is_action_fn = true;
} else if let Some(cache_file_type) = &self.in_cache_file {
// All export functions in a cache file are cache functions
if self.in_export_decl && self.in_action_file {
// All export functions in a server file are actions
is_action_fn = true;
}

if self.in_module_level {
if let Some(cache_file_type) = &self.in_cache_file {
cache_type = Some(cache_file_type.clone());
}
}
Expand Down Expand Up @@ -471,7 +472,10 @@ impl<C: Comments> ServerActions<C> {

self.has_cache = true;
self.has_action = true;
self.export_actions.push(export_name.to_string());

if self.config.is_react_server_layer {
self.export_actions.push(export_name.to_string());
}

let reference_id =
generate_action_id(&self.config.hash_salt, &self.file_name, &export_name);
Expand Down Expand Up @@ -668,7 +672,10 @@ impl<C: Comments> ServerActions<C> {

self.has_cache = true;
self.has_action = true;
self.export_actions.push(cache_name.to_string());

if self.config.is_react_server_layer {
self.export_actions.push(cache_name.to_string());
}

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

Expand Down Expand Up @@ -1659,7 +1666,9 @@ impl<C: Comments> VisitMut for ServerActions<C> {
let mut actions = self.export_actions.clone();

// All exported values are considered as actions if the file is an action file.
if self.in_action_file {
if self.in_action_file
|| self.in_cache_file.is_some() && !self.config.is_react_server_layer
{
actions.extend(self.exported_idents.iter().map(|e| e.1.clone()));
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use cache'

export async function foo() {}
const bar = async () => {}
export { bar }

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

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use cache'

export async function foo() {
return 'data'
}
export async function foo() {}
const bar = async () => {}
export { bar }
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* __next_internal_action_entry_do_not_use__ {"3128060c414d59f8552e4788b846c0d2b7f74743":"$$RSC_SERVER_CACHE_0"} */ import { registerServerReference } from "private-next-rsc-server-reference";
/* __next_internal_action_entry_do_not_use__ {"3128060c414d59f8552e4788b846c0d2b7f74743":"$$RSC_SERVER_CACHE_0","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';
});
export var $$RSC_SERVER_CACHE_0 = $$cache__("default", "3128060c414d59f8552e4788b846c0d2b7f74743", async function foo() {});
export var foo = registerServerReference($$RSC_SERVER_CACHE_0, "3128060c414d59f8552e4788b846c0d2b7f74743", null);
export var $$RSC_SERVER_CACHE_1 = $$cache__("default", "951c375b4a6a6e89d67b743ec5808127cfde405d", async function() {});
const bar = registerServerReference($$RSC_SERVER_CACHE_1, "951c375b4a6a6e89d67b743ec5808127cfde405d", null);
export { bar };
15 changes: 15 additions & 0 deletions test/e2e/app-dir/use-cache/app/imported-from-client/cached.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use cache'

export async function bar() {
const v = Math.random()
console.log(v)
return v
}

const baz = async () => {
const v = Math.random()
console.log(v)
return v
}

export { baz }
32 changes: 32 additions & 0 deletions test/e2e/app-dir/use-cache/app/imported-from-client/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use client'

import { useActionState } from 'react'
import { bar, baz } from './cached'

export default function Page() {
const [result, dispatch] = useActionState<
[number, number],
'submit' | 'reset'
>(
async (_state, event) => {
if (event === 'reset') {
return [0, 0]
}

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

return (
<form action={() => dispatch('submit')}>
<button id="submit-button">Click me</button>
<p>
{result[0]} {result[1]}
</p>
<button id="reset-button" formAction={() => dispatch('reset')}>
Reset
</button>
</form>
)
}
23 changes: 23 additions & 0 deletions test/e2e/app-dir/use-cache/use-cache.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable jest/no-standalone-expect */
import { nextTestSetup } from 'e2e-utils'
import { retry } from 'next-test-utils'

const GENERIC_RSC_ERROR =
'An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.'
Expand Down Expand Up @@ -108,6 +109,28 @@ describe('use-cache', () => {
expect(rand1).toEqual(rand2)
})

it('should cache results for cached funtions imported from client components', async () => {
const browser = await next.browser('/imported-from-client')
expect(await browser.elementByCss('p').text()).toBe('0 0')
await browser.elementById('submit-button').click()

let twoRandomValues: string

await retry(async () => {
twoRandomValues = await browser.elementByCss('p').text()
expect(twoRandomValues).toMatch(/\d\.\d+ \d\.\d+/)
})

await browser.elementById('reset-button').click()
expect(await browser.elementByCss('p').text()).toBe('0 0')

await browser.elementById('submit-button').click()

await retry(async () => {
expect(await browser.elementByCss('p').text()).toBe(twoRandomValues)
})
})

if (isNextStart) {
it('should match the expected revalidate config on the prerender manifest', async () => {
const prerenderManifest = JSON.parse(
Expand Down

0 comments on commit 4370913

Please sign in to comment.