diff --git a/packages/plugin-rsc/examples/use-cache-persistent/callable-cache-plugin.ts b/packages/plugin-rsc/examples/use-cache-persistent/callable-cache-plugin.ts index 269fdcb50..1c6b0cad6 100644 --- a/packages/plugin-rsc/examples/use-cache-persistent/callable-cache-plugin.ts +++ b/packages/plugin-rsc/examples/use-cache-persistent/callable-cache-plugin.ts @@ -54,7 +54,7 @@ export function callableCachePlugin(): Plugin { ]), } return ( - `$$ReactServer.registerServerReference(` + + `$$CacheReactServer.registerServerReference(` + `$$cacheWrapper(${value}, ${JSON.stringify(options)}),` + `${JSON.stringify(reference.referenceKey)},` + `${JSON.stringify(name)})` @@ -68,6 +68,9 @@ export function callableCachePlugin(): Plugin { // and viewport exports. // https://github.com/vercel/next.js/blob/aae4179ac628e55483b62cd023a7e1827dcef122/crates/next-custom-transforms/src/transforms/server_actions.rs#L1914-L1919 filter: (_name, meta) => + // Inline "use server" overrides the file-level cache role and + // is left for the built-in transform. + !hasFunctionDirective(meta, 'use server') && meta.valueNode?.type !== 'ObjectExpression' && meta.valueNode?.type !== 'ArrayExpression', rejectNonAsyncFunction: true, @@ -93,9 +96,14 @@ export function callableCachePlugin(): Plugin { exportNames: 'names' in result ? result.names : result.exportNames, }) cacheModules.add(id) - result.output.prepend( + // Preserve leading directives so a later transform can still recognize + // a file-level role such as "use server". + const importPosition = + ast.body.find((node) => !('directive' in node))?.start ?? code.length + result.output.prependLeft( + importPosition, `import $$cacheWrapper, { encryptCacheCaptures as $$encryptCacheCaptures } from "/src/framework/use-cache-runtime";\n` + - `import * as $$ReactServer from "@vitejs/plugin-rsc/react/rsc/server";\n`, + `import * as $$CacheReactServer from "@vitejs/plugin-rsc/react/rsc/server";\n`, ) return { code: result.output.toString(), @@ -111,12 +119,12 @@ export function callableCachePlugin(): Plugin { meta.valueNode?.type !== 'ArrayExpression', rejectNonAsyncFunction: true, runtime: (name) => - `$$ReactClient.createServerReference(` + + `$$CacheReactClient.createServerReference(` + `${JSON.stringify(reference.referenceKey + '#' + name)},` + - `$$ReactClient.callServer,` + + `$$CacheReactClient.callServer,` + `undefined,` + (this.environment.mode === 'dev' - ? `$$ReactClient.findSourceMapURL,` + ? `$$CacheReactClient.findSourceMapURL,` : `undefined,`) + `${JSON.stringify(name)})`, }) @@ -132,7 +140,7 @@ export function callableCachePlugin(): Plugin { const runtimeEnvironment = environmentName === 'client' ? 'browser' : 'ssr' result.output.prepend( - `import * as $$ReactClient from "@vitejs/plugin-rsc/react/${runtimeEnvironment}";\n`, + `import * as $$CacheReactClient from "@vitejs/plugin-rsc/react/${runtimeEnvironment}";\n`, ) return { code: result.output.toString(), @@ -151,6 +159,27 @@ export function callableCachePlugin(): Plugin { } } +function hasFunctionDirective( + meta: Pick, + directive: string, +): boolean { + const node = meta.valueNode + if ( + (node?.type !== 'FunctionDeclaration' && + node?.type !== 'FunctionExpression' && + node?.type !== 'ArrowFunctionExpression') || + node.body.type !== 'BlockStatement' + ) { + return false + } + return node.body.body.some( + (statement) => + statement.type === 'ExpressionStatement' && + 'directive' in statement && + statement.directive === directive, + ) +} + function getCacheWrapperOptions( meta: Pick, ): Pick {