diff --git a/packages/vinext/src/index.ts b/packages/vinext/src/index.ts index 08d2e23a0..6f5b04353 100644 --- a/packages/vinext/src/index.ts +++ b/packages/vinext/src/index.ts @@ -117,6 +117,10 @@ import { createMiddlewareServerOnlyPlugin } from "./plugins/middleware-server-on import { createOptimizeImportsPlugin } from "./plugins/optimize-imports.js"; import { createDynamicPreloadMetadataPlugin } from "./plugins/dynamic-preload-metadata.js"; import { createOgInlineFetchAssetsPlugin, createOgAssetsPlugin } from "./plugins/og-assets.js"; +import { + createServerFunctionDirectivePlugins, + type ServerFunctionDirectiveContext, +} from "./plugins/server-function-directives.js"; import { generateRouteTypes } from "./typegen.js"; import { mergeOptimizeDepsExclude, @@ -187,16 +191,6 @@ import { randomBytes, randomUUID } from "node:crypto"; import commonjs from "vite-plugin-commonjs"; import { normalizePathSeparators, stripViteModuleQuery } from "./utils/path.js"; -type ServerFunctionDirectiveContext = { - value: string; - name: string; - id: string; - directiveMatch: RegExpMatchArray; - location: "inline" | "module"; - parameters?: { count: number; hasRest: boolean }; - runtime?: string; -}; - function parseUseCacheVariant(directive: string): string { return directive === "use cache" ? "" @@ -943,15 +937,18 @@ export default function vinext(options: VinextOptions = {}): PluginOption[] { } const rscImport = import(pathToFileURL(resolvedRscPath).href); rscPluginPromise = rscImport - .then((mod) => { + .then(async (mod) => { const rsc = mod.default; - return rsc({ + const plugins: Plugin[] = rsc({ entries: { rsc: VIRTUAL_RSC_ENTRY, ssr: VIRTUAL_APP_SSR_ENTRY, client: VIRTUAL_APP_BROWSER_ENTRY, }, - serverFunctionDirectives: [ + }); + const [serverFunctionPlugin] = await createServerFunctionDirectivePlugins({ + projectRoot: earlyBaseDir, + definitions: [ { directive: /^use cache.*$/, test: (code: string) => code.includes("use cache"), @@ -1002,15 +999,7 @@ export default function vinext(options: VinextOptions = {}): PluginOption[] { : ""; return `${runtime}.registerCachedFunction(${value}, ${JSON.stringify(id + ":" + name)}, ${JSON.stringify(variant)}${pageOptions})`; }, - filterExport: ({ - name, - id, - meta, - }: { - name: string; - id: string; - meta: { isFunction?: boolean }; - }) => { + filterExport: ({ name, id, meta }) => { if (meta.isFunction === false) return false; if (/\/(layout|template)\.(tsx?|jsx?|mjs)$/.test(id) && name === "default") { return false; @@ -1019,7 +1008,15 @@ export default function vinext(options: VinextOptions = {}): PluginOption[] { }, }, ], + serverEnvironmentName: "rsc", + browserEnvironmentName: "client", }); + const useServerIndex = plugins.findIndex((plugin) => plugin.name === "rsc:use-server"); + if (useServerIndex === -1 || !serverFunctionPlugin) { + throw new Error("vinext: Failed to locate @vitejs/plugin-rsc use-server plugin."); + } + plugins.splice(useServerIndex, 0, serverFunctionPlugin); + return plugins; }) .catch((cause) => { throw new Error("vinext: Failed to load @vitejs/plugin-rsc.", { diff --git a/packages/vinext/src/plugins/server-function-directives.ts b/packages/vinext/src/plugins/server-function-directives.ts new file mode 100644 index 000000000..0f38eea34 --- /dev/null +++ b/packages/vinext/src/plugins/server-function-directives.ts @@ -0,0 +1,433 @@ +import { createHash } from "node:crypto"; +import fs from "node:fs"; +import { createRequire } from "node:module"; +import path from "node:path"; +import { pathToFileURL } from "node:url"; +import type { SourceMap } from "magic-string"; +import type { Plugin, Rollup, ViteDevServer } from "vite"; +import { parseAstAsync, transformWithOxc } from "vite"; +import { isUnknownRecord } from "../utils/record.js"; +import { escapeRegExp } from "../utils/regex.js"; + +type RscTransforms = typeof import("@vitejs/plugin-rsc/transforms"); +type RscPluginManager = NonNullable< + ReturnType +>["manager"]; +type Program = Parameters[0]; +type ModuleDirective = NonNullable< + Parameters[2]["moduleDirective"] +> & { start?: number }; +type StringDirective = ModuleDirective & { type: "Literal"; value: string }; +type ExportFilter = NonNullable[2]["filter"]>; +type ExportMeta = Parameters[1]; +type FunctionParameters = NonNullable; + +export type ServerFunctionDirectiveContext = { + value: string; + name: string; + id: string; + directiveMatch: RegExpMatchArray; + location: "inline" | "module"; + hasBoundArgs: boolean; + parameters?: FunctionParameters; + runtime?: string; + meta?: ExportMeta; +}; + +export type ServerFunctionDirective = { + directive: string | RegExp; + test?: (code: string) => boolean; + filter?: (id: string) => boolean; + validate?: (context: { id: string; directive: string; location: "inline" | "module" }) => void; + rejectNonAsyncFunction?: boolean; + rejectNonAsyncModule?: boolean; + runtime?: string; + wrap: (context: ServerFunctionDirectiveContext) => string; + filterExport?: (context: { name: string; id: string; meta: ExportMeta }) => boolean; + clientError?: (context: { id: string; environment: string }) => string; +}; + +type Options = { + projectRoot: string; + definitions: ServerFunctionDirective[]; + serverEnvironmentName: string; + browserEnvironmentName: string; +}; + +const SERVER_FUNCTION_DIRECTIVE_MARKER = "/* __vite_rsc_server_function_directives__ */"; + +function resolvePluginRscModule(projectRoot: string, specifier: string): string { + try { + return createRequire(path.join(projectRoot, "package.json")).resolve(specifier); + } catch {} + + try { + return createRequire(import.meta.url).resolve(specifier); + } catch { + throw new Error(`vinext: Installed @vitejs/plugin-rsc does not expose ${specifier}.`); + } +} + +async function parseProgram(code: string): Promise { + return (await parseAstAsync(code)) as unknown as Program; +} + +function matchDirective(value: string, directive: string | RegExp): RegExpMatchArray | undefined { + const pattern = + typeof directive === "string" + ? new RegExp(`^${escapeRegExp(directive)}$`) + : new RegExp(directive.source, directive.flags); + pattern.lastIndex = 0; + return value.match(pattern) ?? undefined; +} + +function isStringLiteral(value: unknown): value is StringDirective { + return isUnknownRecord(value) && value.type === "Literal" && typeof value.value === "string"; +} + +function isExpressionStatement( + value: unknown, +): value is Record & { type: "ExpressionStatement"; expression: unknown } { + return isUnknownRecord(value) && value.type === "ExpressionStatement" && "expression" in value; +} + +function isBlockStatement( + value: unknown, +): value is Record & { type: "BlockStatement"; body: unknown[] } { + return isUnknownRecord(value) && value.type === "BlockStatement" && Array.isArray(value.body); +} + +function findModuleDirective( + ast: Program, + directive: string | RegExp, +): StringDirective | undefined { + for (const node of ast.body) { + if (node.type !== "ExpressionStatement") continue; + if (isStringLiteral(node.expression) && matchDirective(node.expression.value, directive)) { + return node.expression; + } + } +} + +function findInlineDirective( + ast: Program, + directive: string | RegExp, +): StringDirective | undefined { + let result: StringDirective | undefined; + + function visit(value: unknown): void { + if (result) return; + if (Array.isArray(value)) { + for (const child of value) visit(child); + return; + } + if (!isUnknownRecord(value)) return; + + const nodeType = typeof value.type === "string" ? value.type : undefined; + if ( + (nodeType === "FunctionDeclaration" || + nodeType === "FunctionExpression" || + nodeType === "ArrowFunctionExpression") && + isBlockStatement(value.body) + ) { + for (const statement of value.body.body) { + if ( + isExpressionStatement(statement) && + isStringLiteral(statement.expression) && + matchDirective(statement.expression.value, directive) + ) { + result = statement.expression; + return; + } + } + } + + for (const [key, child] of Object.entries(value)) { + if (key === "parent" || key === "loc" || key === "start" || key === "end") continue; + visit(child); + } + } + + visit(ast); + return result; +} + +function hashString(value: string): string { + return createHash("sha256").update(value).digest("hex").slice(0, 12); +} + +function normalizeViteImportAnalysisUrl( + environment: ViteDevServer["environments"][string], + id: string, +): string { + const root = environment.config.root; + const rootPrefix = root.endsWith("/") ? root : `${root}/`; + if (id.startsWith(rootPrefix)) return id.slice(root.length); + + const cleanId = id.split("?", 1)[0] ?? id; + if (path.isAbsolute(cleanId) && fs.existsSync(cleanId)) return path.posix.join("/@fs/", id); + if (id.startsWith(".") || id.startsWith("/")) return id; + return `/@id/${id.replace("\0", "__x00__")}`; +} + +async function expandExportAll( + transforms: RscTransforms, + context: Rollup.TransformPluginContext, + code: string, + ast: Program, + id: string, +): Promise<{ code: string } | undefined> { + return transforms.transformExpandExportAll({ + code, + ast, + importer: id, + resolve: async (source, importer) => (await context.resolve(source, importer))?.id, + load: async (resolvedId) => { + const source = await fs.promises.readFile(resolvedId, "utf8"); + const transformed = await transformWithOxc(source, resolvedId, { sourcemap: false }); + return parseProgram(transformed.code); + }, + }); +} + +export async function createServerFunctionDirectivePlugins(options: Options): Promise { + const rscModulePath = resolvePluginRscModule(options.projectRoot, "@vitejs/plugin-rsc"); + const transformsPath = resolvePluginRscModule( + options.projectRoot, + "@vitejs/plugin-rsc/transforms", + ); + const rscRuntime = pathToFileURL( + resolvePluginRscModule(options.projectRoot, "@vitejs/plugin-rsc/react/rsc"), + ).href; + const browserRuntime = pathToFileURL( + resolvePluginRscModule(options.projectRoot, "@vitejs/plugin-rsc/react/browser"), + ).href; + const ssrRuntime = pathToFileURL( + resolvePluginRscModule(options.projectRoot, "@vitejs/plugin-rsc/react/ssr"), + ).href; + const encryptionRuntime = pathToFileURL( + resolvePluginRscModule(options.projectRoot, "@vitejs/plugin-rsc/utils/encryption-runtime"), + ).href; + const rscModule: typeof import("@vitejs/plugin-rsc") = await import( + pathToFileURL(rscModulePath).href + ); + const transforms: RscTransforms = await import(pathToFileURL(transformsPath).href); + const { getPluginApi } = rscModule; + let manager: RscPluginManager | undefined; + + const transformPlugin: Plugin = { + name: "vinext:server-function-directives", + + configResolved(config) { + manager = getPluginApi(config)?.manager; + }, + + transform: { + async handler(code, id) { + if (code.includes(SERVER_FUNCTION_DIRECTIVE_MARKER)) return; + + const active = options.definitions.filter( + (definition) => + (definition.test?.(code) ?? code.includes("use ")) && + (!definition.filter || definition.filter(id)), + ); + const isServer = this.environment.name === options.serverEnvironmentName; + if (active.length === 0) { + if (isServer && manager) delete manager.serverReferenceMetaMap[id]; + return; + } + if (!manager) { + throw new Error("vinext: failed to access @vitejs/plugin-rsc through getPluginApi()."); + } + + let ast = await parseProgram(code); + const useServerBoundary = transforms.hasDirective(ast.body, "use server"); + if (!isServer && useServerBoundary) return; + + const normalizedId = + manager.config.command === "build" + ? hashString(manager.toRelativeId(id)) + : normalizeViteImportAnalysisUrl( + manager.server.environments[options.serverEnvironmentName], + id, + ); + + if (!isServer) { + for (const definition of active) { + const inlineDirective = findInlineDirective(ast, definition.directive); + if (inlineDirective && definition.clientError) { + throw Object.assign( + new Error(definition.clientError({ id, environment: this.environment.name })), + { pos: inlineDirective.start }, + ); + } + } + + const matches: Array = []; + for (const definition of active) { + const moduleDirective = findModuleDirective(ast, definition.directive); + if (moduleDirective) matches.push([definition, moduleDirective]); + } + if (matches.length === 0) return; + if (matches.length > 1) { + throw Object.assign( + new Error("Multiple server function directives match this module."), + { + pos: matches[1]?.[1].start, + }, + ); + } + + const match = matches[0]; + if (!match) return; + const [, moduleDirective] = match; + const result = transforms.transformDirectiveProxyExport(ast, { + code, + directive: moduleDirective.value, + runtime: (name) => + `$$ReactClient.createServerReference(${JSON.stringify(`${normalizedId}#${name}`)},$$ReactClient.callServer,undefined,${this.environment.mode === "dev" ? "$$ReactClient.findSourceMapURL" : "undefined"},${JSON.stringify(name)})`, + }); + if (!result?.output.hasChanged()) return; + manager.serverReferenceMetaMap[id] = { + importId: id, + referenceKey: normalizedId, + exportNames: result.exportNames, + }; + result.output.prepend( + `${SERVER_FUNCTION_DIRECTIVE_MARKER}\nimport * as $$ReactClient from ${JSON.stringify(this.environment.name === options.browserEnvironmentName ? browserRuntime : ssrRuntime)};\n`, + ); + return { + code: result.output.toString(), + map: result.output.generateMap({ hires: "boundary", source: id }), + }; + } + + const exportNames = new Set(); + let needsReactRuntime = false; + let needsEncryptionRuntime = false; + let outputMap: SourceMap | undefined; + + for (const definition of active) { + const runtimeName = definition.runtime + ? `$$server_function_directive_${hashString(definition.runtime)}` + : undefined; + let runtimeUsed = false; + const getRuntime = () => { + if (runtimeName) runtimeUsed = true; + return runtimeName; + }; + + let moduleDirective = findModuleDirective(ast, definition.directive); + if (moduleDirective) { + if (useServerBoundary) { + throw Object.assign( + new Error( + `A module cannot contain both ${JSON.stringify(moduleDirective.value)} and "use server" directives.`, + ), + { pos: moduleDirective.start }, + ); + } + const expanded = await expandExportAll(transforms, this, code, ast, id); + if (expanded) { + code = expanded.code; + ast = await parseProgram(code); + moduleDirective = findModuleDirective(ast, definition.directive); + } + } + + const moduleMatch = moduleDirective + ? matchDirective(moduleDirective.value, definition.directive) + : undefined; + if (moduleMatch) { + definition.validate?.({ id, directive: moduleMatch[0], location: "module" }); + } + + const result = transforms.transformServerActionServer(code, ast, { + runtime: (value, name) => + `$$ReactServer.registerServerReference(${value}, ${JSON.stringify(normalizedId)}, ${JSON.stringify(name)})`, + directive: definition.directive, + moduleDirective, + moduleRuntime: (value, name, meta) => { + if (!moduleMatch) return value; + needsReactRuntime = true; + 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)})`; + }, + inlineRuntime: (value, name, meta) => { + definition.validate?.({ + id, + directive: meta.directiveMatch[0], + location: "inline", + }); + const wrapped = definition.wrap({ + value, + name, + id, + directiveMatch: meta.directiveMatch, + location: "inline", + hasBoundArgs: meta.hasBoundArgs, + parameters: meta.parameters, + runtime: getRuntime(), + }); + if (useServerBoundary) return wrapped; + + needsReactRuntime = true; + if (meta.hasBoundArgs) { + needsEncryptionRuntime = true; + return `$$ReactServer.registerServerReference((($$wrapped) => async ($$encoded, ...$$args) => $$wrapped(...await __vite_rsc_encryption_runtime.decryptActionBoundArgs($$encoded), ...$$args))(${wrapped}), ${JSON.stringify(normalizedId)}, ${JSON.stringify(name)})`; + } + return `$$ReactServer.registerServerReference(${wrapped}, ${JSON.stringify(normalizedId)}, ${JSON.stringify(name)})`; + }, + filter: (name, meta) => definition.filterExport?.({ name, id, meta }) ?? true, + rejectNonAsyncFunction: definition.rejectNonAsyncFunction, + rejectNonAsyncModule: definition.rejectNonAsyncModule, + encode: (value) => { + needsEncryptionRuntime = true; + return `__vite_rsc_encryption_runtime.encryptActionBoundArgs(${value})`; + }, + stableName: true, + exportWrappedHoist: !useServerBoundary, + detectUseServerModule: false, + rejectForbiddenExpressions: true, + }); + if (!result.output.hasChanged()) continue; + + if (runtimeUsed && definition.runtime && runtimeName) { + result.output.prepend( + `import * as ${runtimeName} from ${JSON.stringify(definition.runtime)};\n`, + ); + } + + const transformedNames = "names" in result ? result.names : result.exportNames; + transformedNames.forEach((name) => exportNames.add(name)); + outputMap = result.output.generateMap({ hires: "boundary", source: id }); + code = result.output.toString(); + ast = await parseProgram(code); + } + + if (!useServerBoundary) { + if (exportNames.size === 0) { + delete manager.serverReferenceMetaMap[id]; + } else { + manager.serverReferenceMetaMap[id] = { + importId: id, + referenceKey: normalizedId, + exportNames: [...exportNames], + }; + } + } + + const imports = [ + needsReactRuntime && `import * as $$ReactServer from ${JSON.stringify(rscRuntime)};`, + needsEncryptionRuntime && + `import * as __vite_rsc_encryption_runtime from ${JSON.stringify(encryptionRuntime)};`, + ].filter(Boolean); + return { + code: `${SERVER_FUNCTION_DIRECTIVE_MARKER}\n${imports.join("\n")}\n${code}`, + map: outputMap, + }; + }, + }, + }; + + return [transformPlugin]; +} diff --git a/tests/app-router-production-server.test.ts b/tests/app-router-production-server.test.ts index 9b4ca4725..84f2ddb0e 100644 --- a/tests/app-router-production-server.test.ts +++ b/tests/app-router-production-server.test.ts @@ -1452,6 +1452,13 @@ describe("App Router Production server (startProdServer)", () => { expect(res.status).toBe(200); const html = await res.text(); + // React Flight encodes server-function props with the `$h` token used by + // this React build's SERVER_DECODE_REFERENCE_PREFIX path. Keep this + // assertion next to the action round-trip so the test proves both halves: + // the payload uses the server-reference encoding and the decoded reference + // resolves through vinext's production manifest below. + expect(html).toContain('\\"getDate\\":\\"$h'); + // The flight payload embeds each cached function prop as a server // reference whose id is "<12-hex normalised key>#". const refIds = [ diff --git a/tests/e2e/app-router-prod/use-cache.spec.ts b/tests/e2e/app-router-prod/use-cache.spec.ts index 627858bdd..ef915601d 100644 --- a/tests/e2e/app-router-prod/use-cache.spec.ts +++ b/tests/e2e/app-router-prod/use-cache.spec.ts @@ -1,6 +1,14 @@ import { expect, test } from "@playwright/test"; test.describe('production "use cache" server function references', () => { + test("invokes file-level cached exports imported by a Client Component", async ({ page }) => { + await page.goto("/use-cache-client-import"); + await page.locator("#call-client-imported-cache").click(); + await expect(page.getByTestId("client-imported-cache-result")).toHaveText( + /^client-cache:direct:[0-9.e+-]+$/, + ); + }); + test("replays cached RSC through SSR and invokes nested functions from the browser", async ({ page, }) => { diff --git a/tests/use-cache-transform.test.ts b/tests/use-cache-transform.test.ts index 1b2455caf..abd1fa7c7 100644 --- a/tests/use-cache-transform.test.ts +++ b/tests/use-cache-transform.test.ts @@ -1,6 +1,6 @@ /** - * Tests the plugin-rsc serverFunctionDirectives integration used for function-level - * "use cache" directives. Vinext supplies cache wrapper expressions; plugin-rsc + * Tests the vinext user-land server function directive integration used for function-level + * "use cache" directives. Vinext owns the directive plugin while plugin-rsc * owns directive discovery, closure hoisting, encryption, reference ids, and * server-reference manifest metadata. */ @@ -47,16 +47,83 @@ async function configurePluginRsc(plugins: Plugin[]) { rsc: { build: { outDir: path.join(APP_FIXTURE_DIR, "dist/rsc") } }, }, }); + const useCachePlugin = plugins.find( + (plugin) => plugin.name === "vinext:server-function-directives", + )!; + unwrapHook(useCachePlugin.configResolved)!.call(useCachePlugin, { plugins }); // oxlint-disable-next-line typescript/no-explicit-any return (minimal as any).api.manager; } describe("plugin-rsc inline use-cache references", () => { + it("preserves user-land reference metadata through rsc:use-server", async () => { + const plugins = await getPlugins(); + const manager = await configurePluginRsc(plugins); + const useCacheIndex = plugins.findIndex( + (candidate) => candidate.name === "vinext:server-function-directives", + ); + const useServerIndex = plugins.findIndex((candidate) => candidate.name === "rsc:use-server"); + expect(useCacheIndex).toBeLessThan(useServerIndex); + + const context = { environment: { name: "rsc", mode: "build" } }; + const transformed = await unwrapHook(plugins[useCacheIndex]!.transform)!.call( + context, + inlineCacheCode, + moduleId, + ); + expect(manager.serverReferenceMetaMap[moduleId]).toBeDefined(); + + await unwrapHook(plugins[useServerIndex]!.transform)!.call( + context, + transformed!.code, + moduleId, + ); + expect(manager.serverReferenceMetaMap[moduleId]).toBeDefined(); + + const ssrContext = { environment: { name: "ssr", mode: "build" } }; + const proxied = await unwrapHook(plugins[useCacheIndex]!.transform)!.call( + ssrContext, + fileCacheCode, + moduleId, + ); + await unwrapHook(plugins[useServerIndex]!.transform)!.call(ssrContext, proxied!.code, moduleId); + expect(manager.serverReferenceMetaMap[moduleId]).toMatchObject({ + importId: moduleId, + exportNames: ["getData"], + }); + }); + + it("matches Vite's dev reference key for files outside the project root", async () => { + const plugins = await getPlugins(); + const manager = await configurePluginRsc(plugins); + manager.config.command = "serve"; + manager.server = { + environments: { + rsc: { + config: { root: APP_FIXTURE_DIR }, + moduleGraph: { getModuleById: () => undefined }, + }, + }, + }; + const plugin = plugins.find( + (candidate) => candidate.name === "vinext:server-function-directives", + )!; + const externalId = import.meta.filename; + const result = await unwrapHook(plugin.transform)!.call( + { environment: { name: "rsc", mode: "dev" } }, + inlineCacheCode, + externalId, + ); + const expectedKey = path.posix.join("/@fs/", externalId); + expect(result!.code).toContain(JSON.stringify(expectedKey)); + expect(manager.serverReferenceMetaMap[externalId].referenceKey).toBe(expectedKey); + }); + it("wraps and registers inline cache functions with plugin-rsc's build reference key", async () => { const plugins = await getPlugins(); const manager = await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; const result = await transform.call( @@ -84,7 +151,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; const original = await transform.call( @@ -109,7 +176,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); const manager = await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; await transform.call( @@ -130,7 +197,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; const closureCode = [ @@ -164,7 +231,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; @@ -182,7 +249,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; const result = await transform.call( @@ -197,7 +264,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; const result = await transform.call( @@ -212,7 +279,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; const result = await transform.call( @@ -227,7 +294,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); const manager = await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; const result = await transform.call( @@ -256,7 +323,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; await expect( @@ -274,7 +341,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; await expect( @@ -315,7 +382,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; const result = await transform.call( @@ -331,7 +398,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; await expect( @@ -349,7 +416,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const context = { environment: { name: "rsc", mode: "build" } }; const source = [ @@ -368,7 +435,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; await expect( @@ -384,7 +451,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const result = await unwrapHook(plugin.transform)!.call( { environment: { name: "rsc", mode: "build" } }, @@ -398,7 +465,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); const manager = await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; const result = await transform.call( @@ -419,7 +486,7 @@ describe("plugin-rsc inline use-cache references", () => { const plugins = await getPlugins(); await configurePluginRsc(plugins); const plugin = plugins.find( - (candidate) => candidate.name === "rsc:server-function-directives", + (candidate) => candidate.name === "vinext:server-function-directives", )!; const transform = unwrapHook(plugin.transform)!; const result = await transform.call(