-
-
Notifications
You must be signed in to change notification settings - Fork 263
Expand file tree
/
Copy pathcustom-server-function-plugin.ts
More file actions
103 lines (95 loc) · 3.57 KB
/
Copy pathcustom-server-function-plugin.ts
File metadata and controls
103 lines (95 loc) · 3.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import { getPluginApi, type RscPluginManager } from '@vitejs/plugin-rsc'
import {
hasDirective,
transformDirectiveProxyExport,
transformHoistInlineDirective,
transformWrapExport,
} from '@vitejs/plugin-rsc/transforms'
import { parseAstAsync, type Plugin } from 'vite'
const directive = 'use custom-server'
const pluginName = 'example:custom-server-function'
// This intentionally mirrors the built-in "use server" pipeline through the
// public integration APIs, but uses a separate directive and plugin name and
// omits export-all expansion and action encryption.
// TODO: Give the custom directive observable runtime semantics so ownership
// swaps can assert the active registration path in addition to claim cleanup.
export function customServerFunctionPlugin(): Plugin {
let manager: RscPluginManager
return {
name: pluginName,
configResolved(config) {
manager = getPluginApi(config)!.manager
},
async transform(code, id) {
const environmentName = this.environment.name
if (!code.includes(directive)) {
manager.serverReferences.deleteClaim(pluginName, id)
return
}
const reference = manager.serverReferences.resolve(id, 'rsc')
const ast = (await parseAstAsync(code)) as unknown as Parameters<
typeof transformHoistInlineDirective
>[1]
if (environmentName === 'rsc') {
const runtime = (value: string, name: string) =>
`$$CustomReactServer.registerServerReference(${value}, ${JSON.stringify(reference.referenceKey)}, ${JSON.stringify(name)})`
const result = hasDirective(ast.body, directive)
? transformWrapExport(code, ast, {
runtime,
rejectNonAsyncFunction: true,
})
: transformHoistInlineDirective(code, ast, {
directive,
runtime,
rejectNonAsyncFunction: true,
})
if (!result.output.hasChanged()) {
manager.serverReferences.deleteClaim(pluginName, id)
return
}
manager.serverReferences.replaceClaim(pluginName, id, {
...reference,
exportNames: 'names' in result ? result.names : result.exportNames,
})
result.output.prepend(
`import * as $$CustomReactServer from "@vitejs/plugin-rsc/react/rsc/server";\n`,
)
return {
code: result.output.toString(),
map: result.output.generateMap({ hires: 'boundary' }),
}
}
const result = transformDirectiveProxyExport(ast, {
code,
directive,
rejectNonAsyncFunction: true,
runtime: (name) =>
`$$CustomReactClient.createServerReference(` +
`${JSON.stringify(reference.referenceKey + '#' + name)},` +
`$$CustomReactClient.callServer,` +
`undefined,` +
(this.environment.mode === 'dev'
? `$$CustomReactClient.findSourceMapURL,`
: `undefined,`) +
`${JSON.stringify(name)})`,
})
if (!result?.output.hasChanged()) {
manager.serverReferences.deleteClaim(pluginName, id)
return
}
manager.serverReferences.replaceClaim(pluginName, id, {
...reference,
exportNames: result.exportNames,
})
const runtimeEnvironment =
environmentName === 'client' ? 'browser' : 'ssr'
result.output.prepend(
`import * as $$CustomReactClient from "@vitejs/plugin-rsc/react/${runtimeEnvironment}";\n`,
)
return {
code: result.output.toString(),
map: result.output.generateMap({ hires: 'boundary' }),
}
},
}
}