From 85fa6cc7d476e7205dcf9e0b5538832960e16b0a Mon Sep 17 00:00:00 2001 From: hi-ogawa-agent <266689927+hi-ogawa-agent@users.noreply.github.com> Date: Fri, 24 Jul 2026 21:23:50 +0900 Subject: [PATCH 1/2] test(rsc): cover composed server directives Co-authored-by: OpenCode --- .../e2e/custom-server-function.test.ts | 6 ++++++ .../src/features/mixed-directives/actions.ts | 3 +++ .../features/mixed-directives/composed-action.ts | 16 ++++++++++++++++ .../src/features/mixed-directives/server.tsx | 10 +++++++++- 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/composed-action.ts diff --git a/packages/plugin-rsc/e2e/custom-server-function.test.ts b/packages/plugin-rsc/e2e/custom-server-function.test.ts index f490f3f67..ec6a017d2 100644 --- a/packages/plugin-rsc/e2e/custom-server-function.test.ts +++ b/packages/plugin-rsc/e2e/custom-server-function.test.ts @@ -92,6 +92,11 @@ function defineTest(f: ReturnType) { await page.getByRole('button', { name: 'Custom: 0' }).click() await expect(page.getByRole('button', { name: 'Custom: 1' })).toBeVisible() + // This action composes file-level "use server" with inline + // "use custom-server" on the exported function. + await page.getByRole('button', { name: 'Composed: 0' }).click() + await expect(page.getByRole('button', { name: 'Composed: 1' })).toBeVisible() + // This module is only imported by a Client Component, so its implementation // reaches the RSC build through the aggregated server reference manifest. await page.getByRole('button', { name: 'From client: 0' }).click() @@ -104,5 +109,6 @@ function defineTest(f: ReturnType) { page.getByRole('button', { name: 'Built-in: 0' }), ).toBeVisible() await expect(page.getByRole('button', { name: 'Custom: 0' })).toBeVisible() + await expect(page.getByRole('button', { name: 'Composed: 0' })).toBeVisible() } } diff --git a/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/actions.ts b/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/actions.ts index 8d559b043..d5378a287 100644 --- a/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/actions.ts +++ b/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/actions.ts @@ -1,3 +1,5 @@ +import { resetComposedCount } from './composed-action.ts' + let builtinCount = 0 let customCount = 0 @@ -21,4 +23,5 @@ export async function resetCounts() { 'use server' builtinCount = 0 customCount = 0 + await resetComposedCount() } diff --git a/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/composed-action.ts b/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/composed-action.ts new file mode 100644 index 000000000..d1696724f --- /dev/null +++ b/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/composed-action.ts @@ -0,0 +1,16 @@ +'use server' + +let composedCount = 0 + +export async function getComposedCount() { + return composedCount +} + +export async function incrementComposed() { + 'use custom-server' + composedCount++ +} + +export async function resetComposedCount() { + composedCount = 0 +} diff --git a/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/server.tsx b/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/server.tsx index 67257b6a8..59c734644 100644 --- a/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/server.tsx +++ b/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/server.tsx @@ -5,9 +5,14 @@ import { incrementCustom, resetCounts, } from './actions.ts' +import { + getComposedCount, + incrementComposed, +} from './composed-action.ts' -export function MixedDirectives() { +export async function MixedDirectives() { const { builtinCount, customCount } = getCounts() + const composedCount = await getComposedCount() return ( <>
@@ -18,6 +23,9 @@ export function MixedDirectives() { {customLabel}: {customCount}
+
+ +
From ae12407bc4be85adb83f6db9fd65f33072d5d061 Mon Sep 17 00:00:00 2001 From: hi-ogawa-agent <266689927+hi-ogawa-agent@users.noreply.github.com> Date: Fri, 24 Jul 2026 21:37:40 +0900 Subject: [PATCH 2/2] test(rsc): cover both directive composition orders Co-authored-by: OpenCode --- .../e2e/custom-server-function.test.ts | 30 +++++++++++++++--- .../features/directive-composition/server.tsx | 31 +++++++++++++++++++ .../use-custom-server-file.ts | 16 ++++++++++ .../directive-composition/use-server-file.ts | 19 ++++++++++++ .../src/features/mixed-directives/actions.ts | 3 -- .../mixed-directives/composed-action.ts | 16 ---------- .../src/features/mixed-directives/server.tsx | 10 +----- .../custom-server-function/src/root.tsx | 2 ++ 8 files changed, 94 insertions(+), 33 deletions(-) create mode 100644 packages/plugin-rsc/examples/custom-server-function/src/features/directive-composition/server.tsx create mode 100644 packages/plugin-rsc/examples/custom-server-function/src/features/directive-composition/use-custom-server-file.ts create mode 100644 packages/plugin-rsc/examples/custom-server-function/src/features/directive-composition/use-server-file.ts delete mode 100644 packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/composed-action.ts diff --git a/packages/plugin-rsc/e2e/custom-server-function.test.ts b/packages/plugin-rsc/e2e/custom-server-function.test.ts index ec6a017d2..75709a64d 100644 --- a/packages/plugin-rsc/e2e/custom-server-function.test.ts +++ b/packages/plugin-rsc/e2e/custom-server-function.test.ts @@ -92,10 +92,21 @@ function defineTest(f: ReturnType) { await page.getByRole('button', { name: 'Custom: 0' }).click() await expect(page.getByRole('button', { name: 'Custom: 1' })).toBeVisible() - // This action composes file-level "use server" with inline - // "use custom-server" on the exported function. - await page.getByRole('button', { name: 'Composed: 0' }).click() - await expect(page.getByRole('button', { name: 'Composed: 1' })).toBeVisible() + await page + .getByRole('button', { name: 'Server file, custom inline: 0' }) + .click() + await expect( + page.getByRole('button', { name: 'Server file, custom inline: 1' }), + ).toBeVisible() + + await page + .getByRole('button', { name: 'Custom server file, server inline: 0' }) + .click() + await expect( + page.getByRole('button', { + name: 'Custom server file, server inline: 1', + }), + ).toBeVisible() // This module is only imported by a Client Component, so its implementation // reaches the RSC build through the aggregated server reference manifest. @@ -109,6 +120,15 @@ function defineTest(f: ReturnType) { page.getByRole('button', { name: 'Built-in: 0' }), ).toBeVisible() await expect(page.getByRole('button', { name: 'Custom: 0' })).toBeVisible() - await expect(page.getByRole('button', { name: 'Composed: 0' })).toBeVisible() + + await page.getByRole('button', { name: 'Reset composition' }).click() + await expect( + page.getByRole('button', { name: 'Server file, custom inline: 0' }), + ).toBeVisible() + await expect( + page.getByRole('button', { + name: 'Custom server file, server inline: 0', + }), + ).toBeVisible() } } diff --git a/packages/plugin-rsc/examples/custom-server-function/src/features/directive-composition/server.tsx b/packages/plugin-rsc/examples/custom-server-function/src/features/directive-composition/server.tsx new file mode 100644 index 000000000..fb2fcdf69 --- /dev/null +++ b/packages/plugin-rsc/examples/custom-server-function/src/features/directive-composition/server.tsx @@ -0,0 +1,31 @@ +import { + getCount as getCustomServerFileCount, + increment as incrementCustomServerFile, +} from './use-custom-server-file.ts' +import { + getCount as getServerFileCount, + increment as incrementServerFile, + reset as resetServerFile, +} from './use-server-file.ts' + +export async function DirectiveComposition() { + const [serverFileCount, customServerFileCount] = await Promise.all([ + getServerFileCount(), + getCustomServerFileCount(), + ]) + return ( + <> +
+ +
+
+ +
+
+ +
+ + ) +} diff --git a/packages/plugin-rsc/examples/custom-server-function/src/features/directive-composition/use-custom-server-file.ts b/packages/plugin-rsc/examples/custom-server-function/src/features/directive-composition/use-custom-server-file.ts new file mode 100644 index 000000000..6803f2813 --- /dev/null +++ b/packages/plugin-rsc/examples/custom-server-function/src/features/directive-composition/use-custom-server-file.ts @@ -0,0 +1,16 @@ +'use custom-server' + +let count = 0 + +export async function getCount() { + return count +} + +export async function increment() { + 'use server' + count++ +} + +export async function reset() { + count = 0 +} diff --git a/packages/plugin-rsc/examples/custom-server-function/src/features/directive-composition/use-server-file.ts b/packages/plugin-rsc/examples/custom-server-function/src/features/directive-composition/use-server-file.ts new file mode 100644 index 000000000..4d5c9c468 --- /dev/null +++ b/packages/plugin-rsc/examples/custom-server-function/src/features/directive-composition/use-server-file.ts @@ -0,0 +1,19 @@ +'use server' + +import { reset as resetCustomServerFile } from './use-custom-server-file.ts' + +let count = 0 + +export async function getCount() { + return count +} + +export async function increment() { + 'use custom-server' + count++ +} + +export async function reset() { + count = 0 + await resetCustomServerFile() +} diff --git a/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/actions.ts b/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/actions.ts index d5378a287..8d559b043 100644 --- a/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/actions.ts +++ b/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/actions.ts @@ -1,5 +1,3 @@ -import { resetComposedCount } from './composed-action.ts' - let builtinCount = 0 let customCount = 0 @@ -23,5 +21,4 @@ export async function resetCounts() { 'use server' builtinCount = 0 customCount = 0 - await resetComposedCount() } diff --git a/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/composed-action.ts b/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/composed-action.ts deleted file mode 100644 index d1696724f..000000000 --- a/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/composed-action.ts +++ /dev/null @@ -1,16 +0,0 @@ -'use server' - -let composedCount = 0 - -export async function getComposedCount() { - return composedCount -} - -export async function incrementComposed() { - 'use custom-server' - composedCount++ -} - -export async function resetComposedCount() { - composedCount = 0 -} diff --git a/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/server.tsx b/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/server.tsx index 59c734644..67257b6a8 100644 --- a/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/server.tsx +++ b/packages/plugin-rsc/examples/custom-server-function/src/features/mixed-directives/server.tsx @@ -5,14 +5,9 @@ import { incrementCustom, resetCounts, } from './actions.ts' -import { - getComposedCount, - incrementComposed, -} from './composed-action.ts' -export async function MixedDirectives() { +export function MixedDirectives() { const { builtinCount, customCount } = getCounts() - const composedCount = await getComposedCount() return ( <>
@@ -23,9 +18,6 @@ export async function MixedDirectives() { {customLabel}: {customCount}
-
- -
diff --git a/packages/plugin-rsc/examples/custom-server-function/src/root.tsx b/packages/plugin-rsc/examples/custom-server-function/src/root.tsx index fbc00dfee..bf2ddac99 100644 --- a/packages/plugin-rsc/examples/custom-server-function/src/root.tsx +++ b/packages/plugin-rsc/examples/custom-server-function/src/root.tsx @@ -1,4 +1,5 @@ import { ActionFromClient } from './features/action-from-client/client.tsx' +import { DirectiveComposition } from './features/directive-composition/server.tsx' import { MixedDirectives } from './features/mixed-directives/server.tsx' export function Root(_props: { url: URL }) { @@ -11,6 +12,7 @@ export function Root(_props: { url: URL }) { +