vite-plugin-react-server 1.1.18 → 1.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/dist/package.json +1 -1
- package/dist/plugin/config/defaults.d.ts +1 -1
- package/dist/plugin/config/defaults.d.ts.map +1 -1
- package/dist/plugin/config/defaults.js +2 -2
- package/dist/plugin/config/defaults.js.map +1 -1
- package/dist/plugin/config/resolveOptions.d.ts.map +1 -1
- package/dist/plugin/config/resolveOptions.js +4 -4
- package/dist/plugin/config/resolveOptions.js.map +1 -1
- package/dist/plugin/helpers/handleServerAction.d.ts +34 -0
- package/dist/plugin/helpers/handleServerAction.d.ts.map +1 -0
- package/dist/plugin/helpers/handleServerAction.js +48 -0
- package/dist/plugin/helpers/handleServerAction.js.map +1 -0
- package/dist/plugin/loader/handleExports.d.ts +16 -10
- package/dist/plugin/loader/handleExports.d.ts.map +1 -1
- package/dist/plugin/loader/handleExports.js +32 -16
- package/dist/plugin/loader/handleExports.js.map +1 -1
- package/dist/plugin/loader/transformModuleIfNeeded.d.ts.map +1 -1
- package/dist/plugin/loader/transformModuleIfNeeded.js +22 -11
- package/dist/plugin/loader/transformModuleIfNeeded.js.map +1 -1
- package/dist/plugin/loader/transformModuleWithPreservedFunctions.d.ts +21 -0
- package/dist/plugin/loader/transformModuleWithPreservedFunctions.d.ts.map +1 -1
- package/dist/plugin/loader/transformModuleWithPreservedFunctions.js +395 -73
- package/dist/plugin/loader/transformModuleWithPreservedFunctions.js.map +1 -1
- package/dist/plugin/loader/types.d.ts +9 -3
- package/dist/plugin/loader/types.d.ts.map +1 -1
- package/dist/plugin/react-client/configureWorkerRequestHandler.d.ts.map +1 -1
- package/dist/plugin/react-client/configureWorkerRequestHandler.js +12 -69
- package/dist/plugin/react-client/configureWorkerRequestHandler.js.map +1 -1
- package/dist/plugin/react-client/handleWorkerServerAction.d.ts +12 -0
- package/dist/plugin/react-client/handleWorkerServerAction.d.ts.map +1 -0
- package/dist/plugin/react-client/handleWorkerServerAction.js +47 -0
- package/dist/plugin/react-client/handleWorkerServerAction.js.map +1 -0
- package/dist/plugin/react-server/configureReactServer.js.map +1 -1
- package/dist/plugin/react-server/handleServerAction.d.ts +1 -1
- package/dist/plugin/react-server/handleServerAction.d.ts.map +1 -1
- package/dist/plugin/react-server/handleServerAction.js +42 -6
- package/dist/plugin/react-server/handleServerAction.js.map +1 -1
- package/dist/plugin/react-server/plugin.js +2 -2
- package/dist/plugin/react-server/plugin.js.map +1 -1
- package/dist/plugin/react-static/plugin.d.ts.map +1 -1
- package/dist/plugin/react-static/plugin.js +10 -2
- package/dist/plugin/react-static/plugin.js.map +1 -1
- package/dist/plugin/source-map/createMappingsSerializer.js +128 -157
- package/dist/plugin/source-map/createMappingsSerializer.js.map +1 -0
- package/dist/plugin/types.d.ts +5 -7
- package/dist/plugin/types.d.ts.map +1 -1
- package/dist/plugin/vendor/types.js +1 -0
- package/dist/plugin/worker/rsc/handleRender.d.ts.map +1 -1
- package/dist/plugin/worker/rsc/handleRender.js +0 -1
- package/dist/plugin/worker/rsc/handleRender.js.map +1 -1
- package/dist/plugin/worker/rsc/handlers.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/plugin/config/defaults.tsx +5 -2
- package/plugin/config/resolveOptions.ts +9 -10
- package/plugin/helpers/handleServerAction.ts +79 -0
- package/plugin/loader/handleExports.ts +50 -40
- package/plugin/loader/transformModuleIfNeeded.ts +50 -15
- package/plugin/loader/transformModuleWithPreservedFunctions.ts +494 -127
- package/plugin/loader/types.ts +12 -4
- package/plugin/react-client/configureWorkerRequestHandler.ts +11 -87
- package/plugin/react-client/handleWorkerServerAction.ts +74 -0
- package/plugin/react-server/configureReactServer.ts +1 -1
- package/plugin/react-server/handleServerAction.ts +49 -12
- package/plugin/react-server/plugin.ts +2 -2
- package/plugin/react-static/plugin.ts +10 -2
- package/plugin/types.ts +6 -7
- package/plugin/vendor/types.ts +1 -0
- package/plugin/worker/rsc/handleRender.ts +0 -2
- package/plugin/worker/rsc/handlers.ts +3 -3
|
@@ -30,12 +30,80 @@ export interface TransformOptions {
|
|
|
30
30
|
program?: Program;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// Helper type for mapping info
|
|
34
|
+
interface MappingInfo {
|
|
35
|
+
generatedLine: number;
|
|
36
|
+
originalLine: number;
|
|
37
|
+
originalColumn: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function generateSourceMap(
|
|
41
|
+
moduleId: string,
|
|
42
|
+
source: string,
|
|
43
|
+
lines: string[],
|
|
44
|
+
mappingInfos: MappingInfo[],
|
|
45
|
+
originalSourceMap?: any
|
|
46
|
+
) {
|
|
47
|
+
const createMapping = createMappingsSerializer();
|
|
48
|
+
let mappings = '';
|
|
49
|
+
|
|
50
|
+
// Create a mapping for each line
|
|
51
|
+
for (let i = 0; i < lines.length; i++) {
|
|
52
|
+
const info = mappingInfos[i] || { generatedLine: i + 1, originalLine: 1, originalColumn: 0 };
|
|
53
|
+
mappings += createMapping(
|
|
54
|
+
info.generatedLine,
|
|
55
|
+
0,
|
|
56
|
+
0, // sourceIndex
|
|
57
|
+
info.originalLine,
|
|
58
|
+
info.originalColumn,
|
|
59
|
+
-1 // nameIndex
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Add a final mapping for the end of the file
|
|
64
|
+
mappings += createMapping(
|
|
65
|
+
lines.length + 1,
|
|
66
|
+
0,
|
|
67
|
+
0,
|
|
68
|
+
source.split('\n').length,
|
|
69
|
+
0,
|
|
70
|
+
-1
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const sourceMap = {
|
|
74
|
+
version: 3,
|
|
75
|
+
file: moduleId,
|
|
76
|
+
sources: originalSourceMap?.sources || [moduleId],
|
|
77
|
+
sourcesContent: originalSourceMap?.sourcesContent || [source],
|
|
78
|
+
mappings,
|
|
79
|
+
sourceRoot: originalSourceMap?.sourceRoot || "",
|
|
80
|
+
names: originalSourceMap?.names || [],
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return `data:application/json;charset=utf-8;base64,${Buffer.from(JSON.stringify(sourceMap)).toString("base64")}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
33
86
|
/**
|
|
34
|
-
*
|
|
87
|
+
* --- React RSC Directive Handling ---
|
|
88
|
+
*
|
|
89
|
+
* 1. 'use client' at file top:
|
|
90
|
+
* - Pass through code as-is (after removing directive).
|
|
91
|
+
* - Do not transform or register anything.
|
|
92
|
+
* - Required for React client features to work.
|
|
93
|
+
* - See: https://react.dev/reference/rsc/use-client
|
|
94
|
+
*
|
|
95
|
+
* 2. 'use server' at file top:
|
|
96
|
+
* - Register all exported async functions as server actions.
|
|
97
|
+
* - See: https://react.dev/reference/rsc/use-server#caveats
|
|
98
|
+
*
|
|
99
|
+
* 3. 'use server' at function top:
|
|
100
|
+
* - Register only that async function as a server action.
|
|
101
|
+
* - See: https://react.dev/reference/rsc/use-server#caveats
|
|
102
|
+
*
|
|
103
|
+
* 4. No directive:
|
|
104
|
+
* - Treat as a normal shared or server-only module.
|
|
105
|
+
* - No special registration or transformation.
|
|
35
106
|
*/
|
|
36
|
-
function createClientReferenceError(name: string): string {
|
|
37
|
-
return `Attempted to call ${name}() from the server but ${name} is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.`;
|
|
38
|
-
}
|
|
39
107
|
|
|
40
108
|
/**
|
|
41
109
|
* Transforms a module for RSC boundaries.
|
|
@@ -59,37 +127,42 @@ export function transformModuleWithPreservedFunctions(
|
|
|
59
127
|
isServerFunction: boolean | RegExpMatchArray | null,
|
|
60
128
|
isClientComponent: boolean | RegExpMatchArray | null
|
|
61
129
|
): string {
|
|
62
|
-
//
|
|
63
|
-
let
|
|
64
|
-
let
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (node.type !== "ExpressionStatement") {
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
130
|
+
// Check for existing source map
|
|
131
|
+
let sourceMappingURL = null;
|
|
132
|
+
let sourceMappingStart = 0;
|
|
133
|
+
let sourceMappingEnd = 0;
|
|
134
|
+
let sourceMappingLines = 0;
|
|
135
|
+
let originalSourceMap = null;
|
|
71
136
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
) {
|
|
80
|
-
directive = node.expression.value;
|
|
81
|
-
}
|
|
137
|
+
// Look for source map comment
|
|
138
|
+
const sourceMapMatch = source.match(/\/\/[#@] sourceMappingURL=(.+)$/m);
|
|
139
|
+
if (sourceMapMatch) {
|
|
140
|
+
sourceMappingURL = sourceMapMatch[1];
|
|
141
|
+
sourceMappingStart = sourceMapMatch.index!;
|
|
142
|
+
sourceMappingEnd = sourceMapMatch.index! + sourceMapMatch[0].length;
|
|
143
|
+
sourceMappingLines = sourceMapMatch[0].split('\n').length - 1;
|
|
82
144
|
|
|
83
|
-
|
|
84
|
-
|
|
145
|
+
// If it's a data URL, parse it
|
|
146
|
+
if (sourceMappingURL.startsWith('data:application/json;base64,')) {
|
|
147
|
+
const base64 = sourceMappingURL.slice('data:application/json;base64,'.length);
|
|
148
|
+
originalSourceMap = JSON.parse(Buffer.from(base64, 'base64').toString());
|
|
85
149
|
}
|
|
86
150
|
}
|
|
87
151
|
|
|
88
|
-
// Remove the
|
|
89
|
-
|
|
90
|
-
|
|
152
|
+
// Remove the old source map if present
|
|
153
|
+
let sourceWithoutMap = source;
|
|
154
|
+
if (sourceMappingStart > 0) {
|
|
155
|
+
sourceWithoutMap = source.slice(0, sourceMappingStart) + '\n'.repeat(sourceMappingLines) + source.slice(sourceMappingEnd);
|
|
91
156
|
}
|
|
92
157
|
|
|
158
|
+
// Remove directives from source code
|
|
159
|
+
let sourceWithoutDirective = sourceWithoutMap;
|
|
160
|
+
let directiveEnd = 0;
|
|
161
|
+
let hasFileLevelServerDirective = false;
|
|
162
|
+
let hasFileLevelClientDirective = false;
|
|
163
|
+
let hasFunctionLevelClientDirective = false;
|
|
164
|
+
let hasFunctionLevelServerDirective = false;
|
|
165
|
+
|
|
93
166
|
// Get export names and create module ID literal
|
|
94
167
|
const { exportNames, exports } = handleExports(
|
|
95
168
|
sourceWithoutDirective,
|
|
@@ -99,14 +172,169 @@ export function transformModuleWithPreservedFunctions(
|
|
|
99
172
|
);
|
|
100
173
|
const moduleIdLiteral = JSON.stringify(moduleId);
|
|
101
174
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
175
|
+
// Helper function to check for directives in a node
|
|
176
|
+
function checkForDirective(node: any): string | null {
|
|
177
|
+
if (node.type === "ExpressionStatement") {
|
|
178
|
+
if ("directive" in node && typeof node.directive === "string") {
|
|
179
|
+
return node.directive;
|
|
180
|
+
} else if (
|
|
181
|
+
node.expression.type === "Literal" &&
|
|
182
|
+
typeof node.expression.value === "string" &&
|
|
183
|
+
(node.expression.value === "use server" || node.expression.value === "use client")
|
|
184
|
+
) {
|
|
185
|
+
return node.expression.value;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Check for file-level and function-level directives
|
|
192
|
+
for (const node of program.body) {
|
|
193
|
+
const directive = checkForDirective(node);
|
|
194
|
+
if (directive) {
|
|
195
|
+
if (directive === "use server") {
|
|
196
|
+
if (node.start === 0) {
|
|
197
|
+
hasFileLevelServerDirective = true;
|
|
198
|
+
} else {
|
|
199
|
+
hasFunctionLevelServerDirective = true;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (directive === "use client") {
|
|
203
|
+
if (node.start === 0) {
|
|
204
|
+
hasFileLevelClientDirective = true;
|
|
205
|
+
} else {
|
|
206
|
+
hasFunctionLevelClientDirective = true;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if ("start" in node && "end" in node) {
|
|
210
|
+
directiveEnd = Math.max(directiveEnd, node.end);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Check for function-level server directives in function bodies
|
|
215
|
+
if (node.type === "ExportNamedDeclaration" && node.declaration?.type === "FunctionDeclaration") {
|
|
216
|
+
const funcNode = node.declaration;
|
|
217
|
+
if (funcNode.body?.body) {
|
|
218
|
+
for (const stmt of funcNode.body.body) {
|
|
219
|
+
const directive = checkForDirective(stmt);
|
|
220
|
+
if (directive === "use server") {
|
|
221
|
+
hasFunctionLevelServerDirective = true;
|
|
222
|
+
// Mark this specific function as having a server directive
|
|
223
|
+
const name = funcNode.id?.name;
|
|
224
|
+
if (name) {
|
|
225
|
+
const exportInfo = exports.get(name);
|
|
226
|
+
if (exportInfo) {
|
|
227
|
+
exportInfo.declaration = exportInfo.declaration?.replace(
|
|
228
|
+
/^export\s+function\s+/,
|
|
229
|
+
'export async function '
|
|
230
|
+
);
|
|
231
|
+
exportInfo.isAsync = true;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
} else if (node.type === "FunctionDeclaration") {
|
|
239
|
+
if (node.body?.body) {
|
|
240
|
+
for (const stmt of node.body.body) {
|
|
241
|
+
const directive = checkForDirective(stmt);
|
|
242
|
+
if (directive === "use server") {
|
|
243
|
+
hasFunctionLevelServerDirective = true;
|
|
244
|
+
// Mark this specific function as having a server directive
|
|
245
|
+
const name = node.id?.name;
|
|
246
|
+
if (name) {
|
|
247
|
+
const exportInfo = exports.get(name);
|
|
248
|
+
if (exportInfo) {
|
|
249
|
+
exportInfo.declaration = exportInfo.declaration?.replace(
|
|
250
|
+
/^export\s+function\s+/,
|
|
251
|
+
'export async function '
|
|
252
|
+
);
|
|
253
|
+
exportInfo.isAsync = true;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
} else if (node.type === "VariableDeclaration") {
|
|
261
|
+
for (const decl of node.declarations) {
|
|
262
|
+
if (decl.init?.type === "FunctionExpression" || decl.init?.type === "ArrowFunctionExpression") {
|
|
263
|
+
const body = decl.init.body;
|
|
264
|
+
if (body.type === "BlockStatement" && body.body) {
|
|
265
|
+
for (const stmt of body.body) {
|
|
266
|
+
const directive = checkForDirective(stmt);
|
|
267
|
+
if (directive === "use server") {
|
|
268
|
+
hasFunctionLevelServerDirective = true;
|
|
269
|
+
const name = decl.id.type === "Identifier" ? decl.id.name : undefined;
|
|
270
|
+
if (name) {
|
|
271
|
+
const exportInfo = exports.get(name);
|
|
272
|
+
if (exportInfo) {
|
|
273
|
+
exportInfo.declaration = exportInfo.declaration?.replace(
|
|
274
|
+
/^export\s+function\s+/,
|
|
275
|
+
'export async function '
|
|
276
|
+
);
|
|
277
|
+
exportInfo.isAsync = true;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Validate directive combinations
|
|
290
|
+
if (hasFileLevelClientDirective && hasFileLevelServerDirective) {
|
|
291
|
+
throw new Error(`Module ${moduleId} cannot have both "use client" and "use server" directives`);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (hasFunctionLevelClientDirective) {
|
|
295
|
+
throw new Error(`Module ${moduleId} cannot have function-level "use client" directives - only file-level is allowed`);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Validate against user's explicit intent
|
|
299
|
+
if (Boolean(isClientComponent) && !hasFileLevelClientDirective) {
|
|
300
|
+
throw new Error(`Module ${moduleId} is marked as a client component but has no "use client" directive`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (Boolean(isServerFunction) && !hasFileLevelServerDirective && !hasFunctionLevelServerDirective) {
|
|
304
|
+
if(process.env['NODE_ENV'] !== "production") {
|
|
305
|
+
console.log("Error for file", moduleId, source);
|
|
306
|
+
}
|
|
307
|
+
throw new Error(`Module ${moduleId} is marked as a server function but has no "use server" directive`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Remove the directive and any whitespace after it
|
|
311
|
+
if (directiveEnd > 0) {
|
|
312
|
+
sourceWithoutDirective = source.slice(directiveEnd).trim();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Validate that there are exports to transform if explicitly marked
|
|
316
|
+
if (Boolean(isClientComponent) && exportNames.length === 0) {
|
|
317
|
+
throw new Error(`Module ${moduleId} is marked as a client component but has no exports to transform`);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (Boolean(isServerFunction) && exportNames.length === 0) {
|
|
321
|
+
throw new Error(`Module ${moduleId} is marked as a server function but has no exports to transform`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// For client modules in server environment, replace with client references
|
|
325
|
+
if (Boolean(isClientComponent)) {
|
|
326
|
+
const output: string[] = [];
|
|
327
|
+
const mappingInfos: MappingInfo[] = [];
|
|
328
|
+
|
|
329
|
+
// Add registerClientReference import
|
|
330
|
+
const clientImport = process.env['NODE_ENV'] === 'production'
|
|
331
|
+
? 'import { registerClientReference } from "react-server-dom-esm/server";'
|
|
332
|
+
: 'import { registerClientReference } from "react-server-dom-esm/server.node";';
|
|
333
|
+
output.push(clientImport);
|
|
334
|
+
mappingInfos.push({ generatedLine: 1, originalLine: 1, originalColumn: 0 });
|
|
108
335
|
|
|
109
336
|
// Register each export
|
|
337
|
+
let lineNum = 2;
|
|
110
338
|
for (const name of exportNames) {
|
|
111
339
|
const exportInfo = exports.get(name);
|
|
112
340
|
if (exportInfo) {
|
|
@@ -115,112 +343,251 @@ export function transformModuleWithPreservedFunctions(
|
|
|
115
343
|
name === "default" && exportInfo.localName
|
|
116
344
|
? exportInfo.localName
|
|
117
345
|
: name;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
346
|
+
if (name === 'default') {
|
|
347
|
+
output.push(
|
|
348
|
+
`export default registerClientReference(function() {` +
|
|
349
|
+
`throw new Error("Attempted to call the default export of ${moduleIdLiteral} from the server but it's on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.");` +
|
|
350
|
+
`}, ${moduleIdLiteral}, "default");`
|
|
351
|
+
);
|
|
352
|
+
} else {
|
|
353
|
+
output.push(
|
|
354
|
+
`export const ${exportName} = registerClientReference(function() {` +
|
|
355
|
+
`throw new Error("Attempted to call ${exportName}() from the server but ${exportName} is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.");` +
|
|
356
|
+
`}, ${moduleIdLiteral}, ${JSON.stringify(name)});`
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
// Create a mapping for each registration line
|
|
360
|
+
mappingInfos.push({
|
|
361
|
+
generatedLine: lineNum,
|
|
362
|
+
originalLine: exportInfo.loc?.line || 1,
|
|
363
|
+
originalColumn: exportInfo.loc?.column || 0
|
|
364
|
+
});
|
|
365
|
+
lineNum++;
|
|
124
366
|
}
|
|
125
367
|
}
|
|
126
368
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
369
|
+
const newClientSource = output.join("\n\n");
|
|
370
|
+
const sourceMapBase64 = generateSourceMap(
|
|
371
|
+
moduleId,
|
|
372
|
+
source,
|
|
373
|
+
output,
|
|
374
|
+
mappingInfos,
|
|
375
|
+
originalSourceMap
|
|
376
|
+
);
|
|
377
|
+
return `${newClientSource}\n//# sourceMappingURL=${sourceMapBase64}`;
|
|
378
|
+
}
|
|
135
379
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
380
|
+
// For server modules in client environment, replace with server references
|
|
381
|
+
if (Boolean(isServerFunction)) {
|
|
382
|
+
// First collect all exports that need registration
|
|
383
|
+
const exportedEntries: Array<{ localName: string; exportedName: string; type: string; loc?: { line: number; column: number } }> = [];
|
|
384
|
+
const localNames = new Set();
|
|
139
385
|
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
386
|
+
// Helper to check if a function has a "use server" directive
|
|
387
|
+
function hasServerDirective(node: any): boolean {
|
|
388
|
+
if (hasFileLevelServerDirective) return true;
|
|
389
|
+
if (node.body?.body) {
|
|
390
|
+
for (const stmt of node.body.body) {
|
|
391
|
+
const directive = checkForDirective(stmt);
|
|
392
|
+
if (directive === "use server") return true;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return false;
|
|
144
396
|
}
|
|
145
397
|
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
createMapping(generatedLine, 0, 0, i + 1, 0, -1); // +1 because we skip directive line
|
|
150
|
-
generatedLine++;
|
|
151
|
-
}
|
|
398
|
+
// First pass: collect exports and remove directives
|
|
399
|
+
let newSource = source;
|
|
400
|
+
let directiveEnd = 0;
|
|
152
401
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
newSource +
|
|
166
|
-
"\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," +
|
|
167
|
-
Buffer.from(JSON.stringify(sourceMap)).toString("base64")
|
|
168
|
-
);
|
|
169
|
-
// end of server module
|
|
170
|
-
}
|
|
171
|
-
if (!!isClientComponent) {
|
|
172
|
-
// For client modules in server environment, register client references
|
|
173
|
-
const imports = [
|
|
174
|
-
'import { registerClientReference } from "react-server-dom-esm/server.node";',
|
|
175
|
-
];
|
|
176
|
-
const declarations: string[] = [];
|
|
402
|
+
for (const node of program.body) {
|
|
403
|
+
// Handle directives
|
|
404
|
+
const directive = checkForDirective(node);
|
|
405
|
+
if (directive === "use server" || directive === "use client") {
|
|
406
|
+
if (node.start === 0) {
|
|
407
|
+
directiveEnd = node.end;
|
|
408
|
+
} else {
|
|
409
|
+
// Remove function-level directive
|
|
410
|
+
newSource = newSource.slice(0, node.start) + newSource.slice(node.end);
|
|
411
|
+
}
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
177
414
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
415
|
+
// Collect exports that need registration, with loc
|
|
416
|
+
switch (node.type) {
|
|
417
|
+
case 'ExportDefaultDeclaration':
|
|
418
|
+
if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
|
|
419
|
+
const name = node.declaration.id.name;
|
|
420
|
+
if (hasServerDirective(node.declaration)) {
|
|
421
|
+
exportedEntries.push({
|
|
422
|
+
localName: name,
|
|
423
|
+
exportedName: 'default',
|
|
424
|
+
type: 'function',
|
|
425
|
+
loc: node.declaration.id.loc?.start || { line: 1, column: 0 }
|
|
426
|
+
});
|
|
427
|
+
localNames.add(name);
|
|
428
|
+
}
|
|
429
|
+
} else if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
|
|
430
|
+
const name = node.declaration.id.name;
|
|
431
|
+
exportedEntries.push({
|
|
432
|
+
localName: name,
|
|
433
|
+
exportedName: 'default',
|
|
434
|
+
type: 'class',
|
|
435
|
+
loc: node.declaration.id.loc?.start || { line: 1, column: 0 }
|
|
436
|
+
});
|
|
437
|
+
localNames.add(name);
|
|
438
|
+
}
|
|
439
|
+
break;
|
|
440
|
+
case 'ExportNamedDeclaration':
|
|
441
|
+
if (node.declaration) {
|
|
442
|
+
if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
|
|
443
|
+
const name = node.declaration.id.name;
|
|
444
|
+
if (hasServerDirective(node.declaration)) {
|
|
445
|
+
exportedEntries.push({
|
|
446
|
+
localName: name,
|
|
447
|
+
exportedName: name,
|
|
448
|
+
type: 'function',
|
|
449
|
+
loc: node.declaration.id.loc?.start || { line: 1, column: 0 }
|
|
450
|
+
});
|
|
451
|
+
localNames.add(name);
|
|
452
|
+
}
|
|
453
|
+
} else if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
|
|
454
|
+
const name = node.declaration.id.name;
|
|
455
|
+
exportedEntries.push({
|
|
456
|
+
localName: name,
|
|
457
|
+
exportedName: name,
|
|
458
|
+
type: 'class',
|
|
459
|
+
loc: node.declaration.id.loc?.start || { line: 1, column: 0 }
|
|
460
|
+
});
|
|
461
|
+
localNames.add(name);
|
|
462
|
+
} else if (node.declaration.type === 'VariableDeclaration') {
|
|
463
|
+
for (const decl of node.declaration.declarations) {
|
|
464
|
+
if (decl.id.type === 'Identifier') {
|
|
465
|
+
const name = decl.id.name;
|
|
466
|
+
if (decl.init) {
|
|
467
|
+
if (decl.init.type === 'FunctionExpression' || decl.init.type === 'ArrowFunctionExpression') {
|
|
468
|
+
if (hasServerDirective(decl.init)) {
|
|
469
|
+
exportedEntries.push({
|
|
470
|
+
localName: name,
|
|
471
|
+
exportedName: name,
|
|
472
|
+
type: 'function',
|
|
473
|
+
loc: decl.id.loc?.start || { line: 1, column: 0 }
|
|
474
|
+
});
|
|
475
|
+
localNames.add(name);
|
|
476
|
+
}
|
|
477
|
+
} else {
|
|
478
|
+
// Register non-function values
|
|
479
|
+
exportedEntries.push({
|
|
480
|
+
localName: name,
|
|
481
|
+
exportedName: name,
|
|
482
|
+
type: 'value',
|
|
483
|
+
loc: decl.id.loc?.start || { line: 1, column: 0 }
|
|
484
|
+
});
|
|
485
|
+
localNames.add(name);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (node.specifiers) {
|
|
493
|
+
for (const spec of node.specifiers) {
|
|
494
|
+
if (spec.type === 'ExportSpecifier') {
|
|
495
|
+
const localName = spec.local.type === 'Identifier' ? spec.local.name : '';
|
|
496
|
+
const exportedName = spec.exported.type === 'Identifier' ? spec.exported.name : '';
|
|
497
|
+
if (localName && exportedName) {
|
|
498
|
+
// Find the original declaration to check for directive
|
|
499
|
+
const originalDecl = program.body.find(n =>
|
|
500
|
+
(n.type === 'FunctionDeclaration' && n.id?.name === localName) ||
|
|
501
|
+
(n.type === 'ClassDeclaration' && n.id?.name === localName) ||
|
|
502
|
+
(n.type === 'VariableDeclaration' && n.declarations.some(d =>
|
|
503
|
+
d.id.type === 'Identifier' && d.id.name === localName
|
|
504
|
+
))
|
|
505
|
+
);
|
|
506
|
+
let loc = { line: 1, column: 0 };
|
|
507
|
+
if (originalDecl) {
|
|
508
|
+
if (originalDecl.type === 'FunctionDeclaration' && hasServerDirective(originalDecl)) {
|
|
509
|
+
loc = originalDecl.id?.loc?.start || loc;
|
|
510
|
+
exportedEntries.push({
|
|
511
|
+
localName,
|
|
512
|
+
exportedName,
|
|
513
|
+
type: 'function',
|
|
514
|
+
loc
|
|
515
|
+
});
|
|
516
|
+
localNames.add(localName);
|
|
517
|
+
} else if (originalDecl.type === 'ClassDeclaration') {
|
|
518
|
+
loc = originalDecl.id?.loc?.start || loc;
|
|
519
|
+
exportedEntries.push({
|
|
520
|
+
localName,
|
|
521
|
+
exportedName,
|
|
522
|
+
type: 'class',
|
|
523
|
+
loc
|
|
524
|
+
});
|
|
525
|
+
localNames.add(localName);
|
|
526
|
+
} else if (originalDecl.type === 'VariableDeclaration') {
|
|
527
|
+
// Find the right declaration
|
|
528
|
+
const decl = originalDecl.declarations.find(d => d.id.type === 'Identifier' && d.id.name === localName);
|
|
529
|
+
loc = decl?.id?.loc?.start || loc;
|
|
530
|
+
exportedEntries.push({
|
|
531
|
+
localName,
|
|
532
|
+
exportedName,
|
|
533
|
+
type: 'value',
|
|
534
|
+
loc
|
|
535
|
+
});
|
|
536
|
+
localNames.add(localName);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
break;
|
|
188
544
|
}
|
|
189
545
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
let mappings = "";
|
|
195
|
-
const createMapping = createMappingsSerializer();
|
|
196
|
-
let generatedLine = 1;
|
|
197
|
-
|
|
198
|
-
// Map the import line to the first line of the original source
|
|
199
|
-
createMapping(generatedLine, 0, 0, 0, 0, -1);
|
|
200
|
-
generatedLine++;
|
|
201
|
-
|
|
202
|
-
// Map the declaration lines to the first line of the original source
|
|
203
|
-
for (let i = 0; i < declarations.length; i++) {
|
|
204
|
-
createMapping(generatedLine, 0, 0, 1, 0, -1);
|
|
205
|
-
generatedLine++;
|
|
546
|
+
|
|
547
|
+
// Remove file-level directive if present
|
|
548
|
+
if (directiveEnd > 0) {
|
|
549
|
+
newSource = newSource.slice(directiveEnd).trim();
|
|
206
550
|
}
|
|
207
551
|
|
|
208
|
-
// Add
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
552
|
+
// Add import and registrations
|
|
553
|
+
const lines: string[] = [];
|
|
554
|
+
const mappingInfos: MappingInfo[] = [];
|
|
555
|
+
|
|
556
|
+
// Add import statement
|
|
557
|
+
const serverImport = process.env['NODE_ENV'] === 'production'
|
|
558
|
+
? 'import { registerServerReference } from "react-server-dom-esm/server";'
|
|
559
|
+
: 'import { registerServerReference } from "react-server-dom-esm/server.node";';
|
|
560
|
+
lines.push(serverImport);
|
|
561
|
+
mappingInfos.push({ generatedLine: 1, originalLine: 1, originalColumn: 0 });
|
|
562
|
+
|
|
563
|
+
// Add the original source code
|
|
564
|
+
lines.push(newSource);
|
|
565
|
+
mappingInfos.push({ generatedLine: 2, originalLine: 1, originalColumn: 0 });
|
|
566
|
+
|
|
567
|
+
// Add registrations after the source code
|
|
568
|
+
let lineNum = lines.length + 1;
|
|
569
|
+
for (const entry of exportedEntries) {
|
|
570
|
+
lines.push(`registerServerReference(${entry.localName}, ${JSON.stringify(moduleId)}, ${JSON.stringify(entry.exportedName)});`);
|
|
571
|
+
// Create a mapping for each registration line
|
|
572
|
+
mappingInfos.push({
|
|
573
|
+
generatedLine: lineNum,
|
|
574
|
+
originalLine: entry.loc?.line || 1,
|
|
575
|
+
originalColumn: entry.loc?.column || 0
|
|
576
|
+
});
|
|
577
|
+
lineNum++;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const newTransformedSource = lines.join('\n');
|
|
581
|
+
const sourceMapBase64 = generateSourceMap(
|
|
582
|
+
moduleId,
|
|
583
|
+
source,
|
|
584
|
+
lines,
|
|
585
|
+
mappingInfos,
|
|
586
|
+
originalSourceMap
|
|
223
587
|
);
|
|
588
|
+
return `${newTransformedSource}\n//# sourceMappingURL=${sourceMapBase64}`;
|
|
224
589
|
}
|
|
225
|
-
|
|
590
|
+
|
|
591
|
+
// For non-server, non-client modules, return as is
|
|
592
|
+
return sourceWithoutMap;
|
|
226
593
|
}
|