Skip to content

Support ast-node-based filter in transformDirectiveProxyExport #1391

Description

@hi-ogawa

We have demo-ed this capability for use cache imported in server, but not when imported in client directly.

// Ordinary values remain available from "use cache" modules without becoming
// callable server references.
export const metadata = { title: 'cached metadata' }
export const tags = ['cache']

We would need similar filtering feature for transformDirectiveProxyExport to support this use case.


3. Non-RSC Proxy Filtering For Known Non-Function Exports

Remaining primitive

Allow transformDirectiveProxyExport to select which module exports become Server Reference proxies using the same static export facts available to the RSC-side wrapper transform.

The gap is concrete for a module-level "use cache" file containing ordinary object/array exports alongside a cached function:

'use cache'

export const metadata = { title: 'page' }
export const tags = ['cache']
export async function cached(value) {
  return value
}

The callable cache example already has this source shape in file-directive-from-server/action.ts. Its RSC branch filters object and array exports before transformWrapExport validation at callable-cache-plugin.ts:37, so metadata and tags remain ordinary values while cached is wrapped.

The non-RSC branch has no equivalent filter. transformDirectiveProxyExport validates every variable initializer and proxies every discovered export at packages/plugin-rsc/src/transforms/proxy-export.ts:62. With the callable example's rejectNonAsyncFunction: true, metadata or tags throws unsupported non async function before cached can become a proxy. Existing E2E coverage avoids this path: the mixed-export fixture is imported only through the server, while the client-imported fixture exports only a function.

The required primitive is a metadata-aware proxy filter or equivalent selected-export contract. For framework-owned "use cache" behavior:

  • Known function exports become proxies.
  • Unknown expressions remain conservatively proxied because their runtime value is unavailable in the client/SSR graph.
  • Known object and array exports are omitted from the proxy module.
  • Direct primitive exports may still be rejected according to directive policy.
  • Returned exportNames and Server Reference claims include only emitted proxies.

This is narrower than server-side classification. The RSC side can already implement typeof value === 'function' ? registerCacheWrapper(value) : value through transformWrapExport without re-evaluating the source initializer.

Next.js usage and rationale

Next.js client fixture 17 contains a module-level "use cache" file exporting an object and array. Its checked client output is empty, proving that known non-function exports do not become Server Reference proxies.

Client fixture 13 proxies known functions and statically unknown expressions from a "use cache" module. The corresponding classifier excludes object, array, and literal expressions from cache runtime wrapping at server_actions.rs:2966.

No single maintained Next.js fixture combines known non-functions and functions in one declaration. Fixtures 13 and 17 plus the shared classifier establish the mixed result: emit proxies for callable or potentially callable exports and omit known object/array exports.

Activity

  1. added theissue type on Aug 4, 2026
  2. changed the issue type fromtoon Aug 4, 2026
  3. changed the title [-]Non-RSC Proxy Filtering For Known Non-Function Exports[/-] [+]Support node-based filter in `transformDirectiveProxyExport`[/+] on Aug 4, 2026
  4. changed the title [-]Support node-based filter in `transformDirectiveProxyExport`[/-] [+]Support ast-node-based filter in `transformDirectiveProxyExport`[/+] on Aug 4, 2026
  5. locked and limited conversation to collaborators on Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions