From fdce6b8c38dba9199f887f659f4ea8146c0bb430 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 9 Oct 2026 20:02:07 +0100 Subject: [PATCH] feat(rsc): expose closure captures in transformHoistInlineDirective meta --- .../plugin-rsc/src/transforms/hoist.test.ts | 29 +++++++++++++++++++ packages/plugin-rsc/src/transforms/hoist.ts | 8 ++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/plugin-rsc/src/transforms/hoist.test.ts b/packages/plugin-rsc/src/transforms/hoist.test.ts index cd91f59ed..501f24933 100644 --- a/packages/plugin-rsc/src/transforms/hoist.test.ts +++ b/packages/plugin-rsc/src/transforms/hoist.test.ts @@ -174,6 +174,35 @@ async function cached(value, { offset }, ...rest) { ]) }) + it('exposes closure captures to the runtime', async () => { + const input = ` +function Page({ tenant }, label) { + async function capturing(query) { + "use cache"; + return [tenant.id, tenant.region, label, query]; + } + async function standalone() { + "use cache"; + return 1; + } +} +` + const ast = await parseAstAsync(input) + const bindVars: Record = {} + transformHoistInlineDirective(input, ast, { + directive: 'use cache', + runtime: (value, name, meta) => { + bindVars[name] = meta.bindVars + return value + }, + }) + + expect(bindVars).toEqual({ + $$hoist_0_capturing: ['tenant', 'label'], + $$hoist_1_standalone: [], + }) + }) + it('ignores strings outside a function directive prologue', async () => { const input = ` async function initialized() { diff --git a/packages/plugin-rsc/src/transforms/hoist.ts b/packages/plugin-rsc/src/transforms/hoist.ts index c38a3740c..d99413859 100644 --- a/packages/plugin-rsc/src/transforms/hoist.ts +++ b/packages/plugin-rsc/src/transforms/hoist.ts @@ -42,6 +42,8 @@ export type TransformHoistInlineDirectiveMeta = { directiveMatch: RegExpMatchArray /** Original source function before closure captures are added as parameters. */ valueNode: ArrowFunctionExpression | FunctionDeclaration | FunctionExpression + /** Closure captures, in the order they are bound ahead of the original parameters. */ + bindVars: string[] } export type TransformHoistInlineDirectiveResult = { @@ -226,7 +228,11 @@ export function transformHoistInlineDirective( const runtimeCode = `/* #__PURE__ */ ${runtime( implementationName, newName, - { directiveMatch: match, valueNode: node }, + { + directiveMatch: match, + valueNode: node, + bindVars: bindVars.map((b) => b.root), + }, )}` if (options.hoistRuntime) { runtimeHoists.push(