@@ -72,6 +72,129 @@ function parseDirective(code) {
7272 return null ;
7373}
7474
75+ // Transform inline 'use server' functions (inside function bodies) into
76+ // registered server references. Module-level 'use server' is handled
77+ // separately by executeModule.
78+ function transformInlineServerActions ( code ) {
79+ if ( code . indexOf ( 'use server' ) === - 1 ) return code ;
80+ var ast ;
81+ try {
82+ ast = acorn . parse ( code , { ecmaVersion : '2024' , sourceType : 'source' } ) ;
83+ } catch ( x ) {
84+ return code ;
85+ }
86+
87+ var edits = [ ] ;
88+ var counter = 0 ;
89+
90+ function visit ( node , fnDepth ) {
91+ if ( ! node || typeof node !== 'object' ) return ;
92+ var isFn =
93+ node . type === 'FunctionDeclaration' ||
94+ node . type === 'FunctionExpression' ||
95+ node . type === 'ArrowFunctionExpression' ;
96+
97+ // Only look for 'use server' inside nested functions (fnDepth > 0)
98+ if (
99+ isFn &&
100+ fnDepth > 0 &&
101+ node . body &&
102+ node . body . type === 'BlockStatement'
103+ ) {
104+ var body = node . body . body ;
105+ for ( var s = 0 ; s < body . length ; s ++ ) {
106+ var stmt = body [ s ] ;
107+ if ( stmt . type !== 'ExpressionStatement' ) break ;
108+ if ( stmt . directive === 'use server' ) {
109+ edits . push ( {
110+ funcStart : node . start ,
111+ funcEnd : node . end ,
112+ dStart : stmt . start ,
113+ dEnd : stmt . end ,
114+ name : node . id ? node . id . name : 'action' + counter ,
115+ isDecl : node . type === 'FunctionDeclaration' ,
116+ } ) ;
117+ counter ++ ;
118+ return ; // don't recurse into this function
119+ }
120+ if ( ! stmt . directive ) break ;
121+ }
122+ }
123+
124+ var nextDepth = isFn ? fnDepth + 1 : fnDepth ;
125+ for ( var key in node ) {
126+ if ( key === 'start' || key === 'end' || key === 'type' ) continue ;
127+ var child = node [ key ] ;
128+ if ( Array . isArray ( child ) ) {
129+ for ( var i = 0 ; i < child . length ; i ++ ) {
130+ if ( child [ i ] && typeof child [ i ] . type === 'string' ) {
131+ visit ( child [ i ] , nextDepth ) ;
132+ }
133+ }
134+ } else if ( child && typeof child . type === 'string' ) {
135+ visit ( child , nextDepth ) ;
136+ }
137+ }
138+ }
139+
140+ ast . body . forEach ( function ( stmt ) {
141+ visit ( stmt , 0 ) ;
142+ } ) ;
143+ if ( edits . length === 0 ) return code ;
144+
145+ // Apply in reverse order to preserve positions
146+ edits . sort ( function ( a , b ) {
147+ return b . funcStart - a . funcStart ;
148+ } ) ;
149+
150+ var result = code ;
151+ for ( var i = 0 ; i < edits . length ; i ++ ) {
152+ var e = edits [ i ] ;
153+ // Remove the 'use server' directive + trailing whitespace
154+ var dEnd = e . dEnd ;
155+ var ch = result . charAt ( dEnd ) ;
156+ while (
157+ dEnd < result . length &&
158+ ( ch === ' ' || ch === '\n' || ch === '\r' || ch === '\t' )
159+ ) {
160+ dEnd ++ ;
161+ ch = result . charAt ( dEnd ) ;
162+ }
163+ result = result . slice ( 0 , e . dStart ) + result . slice ( dEnd ) ;
164+ var removed = dEnd - e . dStart ;
165+ var adjEnd = e . funcEnd - removed ;
166+
167+ // Wrap function with __rsa (register server action)
168+ var funcCode = result . slice ( e . funcStart , adjEnd ) ;
169+ if ( e . isDecl ) {
170+ // async function foo() { ... } →
171+ // var foo = __rsa(async function foo() { ... }, 'foo');
172+ result =
173+ result . slice ( 0 , e . funcStart ) +
174+ 'var ' +
175+ e . name +
176+ ' = __rsa(' +
177+ funcCode +
178+ ", '" +
179+ e . name +
180+ "');" +
181+ result . slice ( adjEnd ) ;
182+ } else {
183+ // expression/arrow: just wrap in __rsa(...)
184+ result =
185+ result . slice ( 0 , e . funcStart ) +
186+ '__rsa(' +
187+ funcCode +
188+ ", '" +
189+ e . name +
190+ "')" +
191+ result . slice ( adjEnd ) ;
192+ }
193+ }
194+
195+ return result ;
196+ }
197+
75198// Resolve relative paths (e.g., './Counter.js' from '/src/App.js' → '/src/Counter.js')
76199function resolvePath ( from , to ) {
77200 if ( ! to . startsWith ( '.' ) ) return to ;
@@ -171,12 +294,22 @@ function deploy(files) {
171294 return executeModule ( resolved ) ;
172295 } ;
173296
174- new Function ( 'module' , 'exports' , 'require' , 'React' , compiled [ filePath ] ) (
175- mod ,
176- mod . exports ,
177- localRequire ,
178- React
179- ) ;
297+ // Transform inline 'use server' functions before execution
298+ var codeToExecute = compiled [ filePath ] ;
299+ if ( directive !== 'use server' ) {
300+ codeToExecute = transformInlineServerActions ( codeToExecute ) ;
301+ }
302+
303+ new Function (
304+ 'module' ,
305+ 'exports' ,
306+ 'require' ,
307+ 'React' ,
308+ '__rsa' ,
309+ codeToExecute
310+ ) ( mod , mod . exports , localRequire , React , function ( fn , name ) {
311+ return registerServerReference ( fn , filePath , name ) ;
312+ } ) ;
180313
181314 modules [ filePath ] = mod . exports ;
182315
@@ -259,7 +392,12 @@ function render() {
259392 var App = deployed . module . default || deployed . module ;
260393 var element = React . createElement ( App ) ;
261394 return RSDWServer . renderToReadableStream ( element , createModuleMap ( ) , {
262- onError : console . error ,
395+ onError : function ( err ) {
396+ var msg = err && err . message ? err . message : String ( err ) ;
397+ var stack = err && err . stack ? err . stack : '' ;
398+ console . error ( '[RSC Server Error]' , msg , stack ) ;
399+ return msg ;
400+ } ,
263401 } ) ;
264402}
265403
@@ -289,7 +427,15 @@ function callAction(actionId, encodedArgs) {
289427 var App = deployed . module . default || deployed . module ;
290428 return RSDWServer . renderToReadableStream (
291429 { root : React . createElement ( App ) , returnValue : resultPromise } ,
292- createModuleMap ( )
430+ createModuleMap ( ) ,
431+ {
432+ onError : function ( err ) {
433+ var msg = err && err . message ? err . message : String ( err ) ;
434+ var stack = err && err . stack ? err . stack : '' ;
435+ console . error ( '[RSC Server Error]' , msg , stack ) ;
436+ return msg ;
437+ } ,
438+ }
293439 ) ;
294440 } ) ;
295441 } ) ;
0 commit comments