Skip to content

Commit 8595871

Browse files
committed
feat(rsc): import custom directive runtimes
1 parent c217ebc commit 8595871

2 files changed

Lines changed: 37 additions & 1 deletion

File tree

packages/plugin-rsc/src/plugins/server-function-directives.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,23 @@ export async function action() {
333333
).rejects.toThrow('non async function')
334334
})
335335

336+
it('imports directive runtimes for synchronous wrapper expressions', async () => {
337+
const { run } = createHarness([
338+
cacheDirective({
339+
runtime: '/cache-runtime.js',
340+
wrap: ({ value, runtime }) => `${runtime}.cache(${value})`,
341+
}),
342+
])
343+
const result = await run(`
344+
export class CacheClass {
345+
static async getData() { "use cache"; return 1 }
346+
}
347+
`)
348+
expect(result?.code).toContain('from "/cache-runtime.js"')
349+
expect(result?.code).toContain('.cache($$hoist_')
350+
expect(result?.code).not.toContain('await import')
351+
})
352+
336353
it.each(['this', 'super', 'arguments'] as const)(
337354
'rejects %s inside inline directive functions',
338355
async (expression) => {

packages/plugin-rsc/src/plugins/server-function-directives.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export type ServerFunctionDirectiveContext = {
3333
hasBoundArgs: boolean
3434
/** Declared parameter shape when statically known. */
3535
parameters?: FunctionParameters
36+
/** Imported runtime namespace when `runtime` is configured. */
37+
runtime?: string
3638
/** Export metadata. Only present for module-level directives. */
3739
meta?: Parameters<TransformWrapExportFilter>[1]
3840
}
@@ -54,6 +56,8 @@ export type ServerFunctionDirective = {
5456
rejectNonAsyncFunction?: boolean
5557
/** Overrides synchronous-function validation for module-level directives. */
5658
rejectNonAsyncModule?: boolean
59+
/** Module imported as a namespace for use by `wrap`. */
60+
runtime?: string
5761
/** Returns the runtime expression used to wrap the server function. */
5862
wrap: (context: ServerFunctionDirectiveContext) => string
5963
/** Selects which exports a module-level directive wraps and registers. */
@@ -261,6 +265,14 @@ export function vitePluginServerFunctionDirectives(options: Options): Plugin {
261265
| undefined
262266

263267
for (const definition of active) {
268+
const runtimeName = definition.runtime
269+
? `$$server_function_directive_${hashString(definition.runtime)}`
270+
: undefined
271+
let runtimeUsed = false
272+
const getRuntime = () => {
273+
if (runtimeName) runtimeUsed = true
274+
return runtimeName
275+
}
264276
let moduleDirective = findModuleDirective(ast, definition.directive)
265277
if (moduleDirective) {
266278
if (useServerBoundary) {
@@ -297,7 +309,7 @@ export function vitePluginServerFunctionDirectives(options: Options): Plugin {
297309
moduleDirective,
298310
moduleRuntime: (value, name, meta) => {
299311
if (!moduleMatch) return value
300-
return `$$ReactServer.registerServerReference(${definition.wrap({ value, name, id, directiveMatch: moduleMatch, location: 'module', hasBoundArgs: false, parameters: meta.parameters, meta })}, ${JSON.stringify(normalizedId)}, ${JSON.stringify(name)})`
312+
return `$$ReactServer.registerServerReference(${definition.wrap({ value, name, id, directiveMatch: moduleMatch, location: 'module', hasBoundArgs: false, parameters: meta.parameters, runtime: getRuntime(), meta })}, ${JSON.stringify(normalizedId)}, ${JSON.stringify(name)})`
301313
},
302314
inlineRuntime: (value, name, meta) => {
303315
definition.validate?.({
@@ -314,6 +326,7 @@ export function vitePluginServerFunctionDirectives(options: Options): Plugin {
314326
location: 'inline',
315327
hasBoundArgs: meta.hasBoundArgs,
316328
parameters: meta.parameters,
329+
runtime: getRuntime(),
317330
})
318331

319332
if (useServerBoundary) return wrapped
@@ -339,6 +352,12 @@ export function vitePluginServerFunctionDirectives(options: Options): Plugin {
339352
})
340353
if (!result.output.hasChanged()) continue
341354

355+
if (runtimeUsed && definition.runtime && runtimeName) {
356+
result.output.prepend(
357+
`import * as ${runtimeName} from ${JSON.stringify(definition.runtime)};\n`,
358+
)
359+
}
360+
342361
const resultExportNames =
343362
'names' in result ? result.names : result.exportNames
344363
resultExportNames.forEach((name) => exportNames.add(name))

0 commit comments

Comments
 (0)