Skip to content

Commit d995aa3

Browse files
fix: decouple server functions caller and provider (#5712)
1 parent 35382b2 commit d995aa3

File tree

23 files changed

+214
-214
lines changed

23 files changed

+214
-214
lines changed

packages/directive-functions-plugin/src/compilers.ts

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,34 +41,27 @@ export type ReplacerFn = (opts: {
4141

4242
export type CompileDirectivesOpts = ParseAstOptions & {
4343
directive: string
44-
directiveLabel: string
4544
getRuntimeCode?: (opts: {
4645
directiveFnsById: Record<string, DirectiveFn>
4746
}) => string
4847
generateFunctionId: GenerateFunctionIdFn
4948
replacer: ReplacerFn
5049
filename: string
5150
root: string
52-
}
53-
54-
function buildDirectiveSplitParam(opts: CompileDirectivesOpts) {
55-
return `tsr-directive-${opts.directive.replace(/[^a-zA-Z0-9]/g, '-')}`
51+
isDirectiveSplitParam: boolean
52+
directiveSplitParam: string
5653
}
5754

5855
export function compileDirectives(opts: CompileDirectivesOpts): {
5956
compiledResult: GeneratorResult
6057
directiveFnsById: Record<string, DirectiveFn>
61-
isDirectiveSplitParam: boolean
6258
} {
63-
const directiveSplitParam = buildDirectiveSplitParam(opts)
64-
const isDirectiveSplitParam = opts.filename.includes(directiveSplitParam)
65-
6659
const ast = parseAst(opts)
6760
const refIdents = findReferencedIdentifiers(ast)
6861
const directiveFnsById = findDirectives(ast, {
6962
...opts,
70-
directiveSplitParam,
71-
isDirectiveSplitParam,
63+
directiveSplitParam: opts.directiveSplitParam,
64+
isDirectiveSplitParam: opts.isDirectiveSplitParam,
7265
})
7366

7467
// Add runtime code if there are directives
@@ -84,7 +77,7 @@ export function compileDirectives(opts: CompileDirectivesOpts): {
8477
// If we are in the source file, we need to remove all exports
8578
// then make sure that all of our functions are exported under their
8679
// directive name
87-
if (isDirectiveSplitParam) {
80+
if (opts.isDirectiveSplitParam) {
8881
safeRemoveExports(ast)
8982

9083
// Export a single object with all of the functions
@@ -113,13 +106,12 @@ export function compileDirectives(opts: CompileDirectivesOpts): {
113106
return {
114107
compiledResult,
115108
directiveFnsById,
116-
isDirectiveSplitParam,
117109
}
118110
}
119111

120112
function findNearestVariableName(
121113
path: babel.NodePath,
122-
directiveLabel: string,
114+
directive: string,
123115
): string {
124116
let currentPath: babel.NodePath | null = path
125117
const nameParts: Array<string> = []
@@ -189,7 +181,7 @@ function findNearestVariableName(
189181
babel.types.isObjectMethod(currentPath.node)
190182
) {
191183
throw new Error(
192-
`${directiveLabel} in ClassMethod or ObjectMethod not supported`,
184+
`"${directive}" in ClassMethod or ObjectMethod not supported`,
193185
)
194186
}
195187

@@ -219,7 +211,6 @@ export function findDirectives(
219211
ast: babel.types.File,
220212
opts: ParseAstOptions & {
221213
directive: string
222-
directiveLabel: string
223214
replacer?: ReplacerFn
224215
generateFunctionId: GenerateFunctionIdFn
225216
directiveSplitParam: string
@@ -320,7 +311,7 @@ export function findDirectives(
320311
throw codeFrameError(
321312
opts.code,
322313
nearestBlock.node.loc,
323-
`${opts.directiveLabel}s cannot be nested in other blocks or functions`,
314+
`"${opts.directive}" cannot be nested in other blocks or functions`,
324315
)
325316
}
326317

@@ -335,7 +326,7 @@ export function findDirectives(
335326
throw codeFrameError(
336327
opts.code,
337328
directiveFn.node.loc,
338-
`${opts.directiveLabel}s must be function declarations or function expressions`,
329+
`"${opts.directive}" must be function declarations or function expressions`,
339330
)
340331
}
341332

@@ -397,7 +388,7 @@ export function findDirectives(
397388
}
398389

399390
// Find the nearest variable name
400-
let functionName = findNearestVariableName(directiveFn, opts.directiveLabel)
391+
let functionName = findNearestVariableName(directiveFn, opts.directive)
401392

402393
const incrementFunctionNameVersion = (functionName: string) => {
403394
const [realReferenceName, count] = functionName.split(/_(\d+)$/)

packages/directive-functions-plugin/src/index.ts

Lines changed: 64 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -23,136 +23,104 @@ export type {
2323
export type DirectiveFunctionsViteEnvOptions = Pick<
2424
CompileDirectivesOpts,
2525
'getRuntimeCode' | 'replacer'
26-
> & {
27-
envLabel: string
26+
>
27+
export type DirectiveFunctionsViteOptions = DirectiveFunctionsViteEnvOptions & {
28+
directive: string
29+
onDirectiveFnsById?: (directiveFnsById: Record<string, DirectiveFn>) => void
30+
generateFunctionId: GenerateFunctionIdFn
2831
}
2932

30-
export type DirectiveFunctionsViteOptions = Pick<
31-
CompileDirectivesOpts,
32-
'directive' | 'directiveLabel'
33-
> &
34-
DirectiveFunctionsViteEnvOptions & {
35-
onDirectiveFnsById?: (directiveFnsById: Record<string, DirectiveFn>) => void
36-
generateFunctionId: GenerateFunctionIdFn
37-
}
38-
3933
const createDirectiveRx = (directive: string) =>
4034
new RegExp(`"${directive}"|'${directive}'`, 'gm')
4135

42-
export type DirectiveFunctionsVitePluginEnvOptions = Pick<
43-
CompileDirectivesOpts,
44-
'directive' | 'directiveLabel'
45-
> & {
46-
environments: {
47-
client: DirectiveFunctionsViteEnvOptions & { envName?: string }
48-
server: DirectiveFunctionsViteEnvOptions & { envName?: string }
49-
}
36+
export type DirectiveFunctionsVitePluginEnvOptions = {
37+
directive: string
38+
callers: Array<DirectiveFunctionsViteEnvOptions & { envName: string }>
39+
provider: DirectiveFunctionsViteEnvOptions & { envName: string }
5040
onDirectiveFnsById?: (directiveFnsById: Record<string, DirectiveFn>) => void
5141
generateFunctionId: GenerateFunctionIdFn
5242
}
5343

44+
function buildDirectiveSplitParam(directive: string) {
45+
return `tsr-directive-${directive.replace(/[^a-zA-Z0-9]/g, '-')}`
46+
}
47+
5448
export function TanStackDirectiveFunctionsPluginEnv(
5549
opts: DirectiveFunctionsVitePluginEnvOptions,
5650
): Plugin {
57-
opts = {
58-
...opts,
59-
environments: {
60-
client: {
61-
envName: 'client',
62-
...opts.environments.client,
63-
},
64-
server: {
65-
envName: 'server',
66-
...opts.environments.server,
67-
},
68-
},
69-
}
70-
7151
let root: string = process.cwd()
7252

7353
const directiveRx = createDirectiveRx(opts.directive)
7454

55+
const appliedEnvironments = new Set([
56+
...opts.callers.map((c) => c.envName),
57+
opts.provider.envName,
58+
])
59+
60+
const directiveSplitParam = buildDirectiveSplitParam(opts.directive)
61+
7562
return {
7663
name: 'tanstack-start-directive-vite-plugin',
7764
enforce: 'pre',
7865
buildStart() {
7966
root = this.environment.config.root
8067
},
8168
applyToEnvironment(env) {
82-
return [
83-
opts.environments.client.envName,
84-
opts.environments.server.envName,
85-
].includes(env.name)
69+
return appliedEnvironments.has(env.name)
8670
},
8771
transform: {
8872
filter: {
8973
code: directiveRx,
9074
},
9175
handler(code, id) {
92-
const envOptions = [
93-
opts.environments.client,
94-
opts.environments.server,
95-
].find((e) => e.envName === this.environment.name)
96-
97-
if (!envOptions) {
98-
throw new Error(`Environment ${this.environment.name} not found`)
76+
const url = pathToFileURL(id)
77+
url.searchParams.delete('v')
78+
id = fileURLToPath(url).replace(/\\/g, '/')
79+
80+
const isDirectiveSplitParam = id.includes(directiveSplitParam)
81+
82+
let envOptions: DirectiveFunctionsViteEnvOptions & { envName: string }
83+
if (isDirectiveSplitParam) {
84+
envOptions = opts.provider
85+
if (debug)
86+
console.info(
87+
`Compiling Directives for provider in environment ${envOptions.envName}: `,
88+
id,
89+
)
90+
} else {
91+
envOptions = opts.callers.find(
92+
(e) => e.envName === this.environment.name,
93+
)!
94+
if (debug)
95+
console.info(
96+
`Compiling Directives for caller in environment ${envOptions.envName}: `,
97+
id,
98+
)
9999
}
100-
101-
return transformCode({
102-
...opts,
103-
...envOptions,
100+
const { compiledResult, directiveFnsById } = compileDirectives({
101+
directive: opts.directive,
102+
getRuntimeCode: envOptions.getRuntimeCode,
103+
generateFunctionId: opts.generateFunctionId,
104+
replacer: envOptions.replacer,
104105
code,
105-
id,
106106
root,
107+
filename: id,
108+
directiveSplitParam,
109+
isDirectiveSplitParam,
107110
})
108-
},
109-
},
110-
}
111-
}
111+
// when we process a file with a directive split param, we have already encountered the directives in that file
112+
// (otherwise we wouldn't have gotten here)
113+
if (!isDirectiveSplitParam) {
114+
opts.onDirectiveFnsById?.(directiveFnsById)
115+
}
112116

113-
function transformCode({
114-
code,
115-
id,
116-
envLabel,
117-
directive,
118-
directiveLabel,
119-
getRuntimeCode,
120-
generateFunctionId,
121-
replacer,
122-
onDirectiveFnsById,
123-
root,
124-
}: DirectiveFunctionsViteOptions & {
125-
code: string
126-
id: string
127-
root: string
128-
}) {
129-
const url = pathToFileURL(id)
130-
url.searchParams.delete('v')
131-
id = fileURLToPath(url).replace(/\\/g, '/')
132-
133-
if (debug) console.info(`${envLabel}: Compiling Directives: `, id)
134-
135-
const { compiledResult, directiveFnsById, isDirectiveSplitParam } =
136-
compileDirectives({
137-
directive,
138-
directiveLabel,
139-
getRuntimeCode,
140-
generateFunctionId,
141-
replacer,
142-
code,
143-
root,
144-
filename: id,
145-
})
146-
// when we process a file with a directive split param, we have already encountered the directives in that file
147-
// (otherwise we wouldn't have gotten here)
148-
if (!isDirectiveSplitParam) {
149-
onDirectiveFnsById?.(directiveFnsById)
150-
}
117+
if (debug) {
118+
logDiff(code, compiledResult.code)
119+
console.log('Output:\n', compiledResult.code + '\n\n')
120+
}
151121

152-
if (debug) {
153-
logDiff(code, compiledResult.code)
154-
console.log('Output:\n', compiledResult.code + '\n\n')
122+
return compiledResult
123+
},
124+
},
155125
}
156-
157-
return compiledResult
158126
}

packages/directive-functions-plugin/tests/compiler.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,27 +19,28 @@ const generateFunctionId: CompileDirectivesOpts['generateFunctionId'] = (
1919

2020
const clientConfig: Omit<CompileDirectivesOpts, 'code'> = {
2121
directive: 'use server',
22-
directiveLabel: 'Server function',
2322
root: './test-files',
2423
filename: './test-files/test.ts',
2524
getRuntimeCode: () => 'import { createClientRpc } from "my-rpc-lib-client"',
2625
generateFunctionId,
2726
replacer: (opts) => `createClientRpc(${JSON.stringify(opts.functionId)})`,
27+
directiveSplitParam: 'tsr-directive-use-server',
28+
isDirectiveSplitParam: false,
2829
}
2930

3031
const ssrConfig: Omit<CompileDirectivesOpts, 'code'> = {
3132
directive: 'use server',
32-
directiveLabel: 'Server function',
3333
root: './test-files',
3434
filename: './test-files/test.ts',
3535
getRuntimeCode: () => 'import { createSsrRpc } from "my-rpc-lib-server"',
3636
generateFunctionId,
3737
replacer: (opts) => `createSsrRpc(${JSON.stringify(opts.functionId)})`,
38+
directiveSplitParam: 'tsr-directive-use-server',
39+
isDirectiveSplitParam: false,
3840
}
3941

4042
const serverConfig: Omit<CompileDirectivesOpts, 'code'> = {
4143
directive: 'use server',
42-
directiveLabel: 'Server function',
4344
root: './test-files',
4445
filename: './test-files/test.ts',
4546
getRuntimeCode: () => 'import { createServerRpc } from "my-rpc-lib-server"',
@@ -52,6 +53,8 @@ const serverConfig: Omit<CompileDirectivesOpts, 'code'> = {
5253
// For any other server functions the split function may reference,
5354
// we use the splitImportFn which is a dynamic import of the split file.
5455
`createServerRpc(${JSON.stringify(opts.functionId)}, ${opts.fn})`,
56+
directiveSplitParam: 'tsr-directive-use-server',
57+
isDirectiveSplitParam: true,
5558
}
5659

5760
describe('server function compilation', () => {

packages/react-start/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@
6262
"default": "./dist/esm/server-rpc.js"
6363
}
6464
},
65+
"./ssr-rpc": {
66+
"import": {
67+
"types": "./dist/esm/ssr-rpc.d.ts",
68+
"default": "./dist/esm/ssr-rpc.js"
69+
}
70+
},
6571
"./plugin/vite": {
6672
"import": {
6773
"types": "./dist/esm/plugin/vite.d.ts",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { createSsrRpc } from '@tanstack/start-server-core/createSsrRpc'

packages/react-start/vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export default mergeConfig(
2929
'./src/client-rpc.ts',
3030
'./src/server.tsx',
3131
'./src/server-rpc.ts',
32+
'./src/ssr-rpc.ts',
3233
'./src/plugin/vite.ts',
3334
],
3435
externalDeps: [

0 commit comments

Comments
 (0)