22// Runs inside the Sandpack iframe. Orchestrates the RSC pipeline:
33// 1. Creates a Web Worker from pre-bundled server runtime
44// 2. Receives file updates from parent (RscFileBridge) via postMessage
5- // 3. Classifies files by directive, sends raw source to Worker
5+ // 3. Sends all user files to Worker for directive detection + compilation
66// 4. Worker compiles with Sucrase + executes, sends back Flight chunks
77// 5. Renders the Flight stream result with React
88
@@ -57,6 +57,36 @@ export function initClient() {
5757 root . render ( jsx ( Root , { initialPromise : initialPromise } ) ) ;
5858 } ) ;
5959
60+ // Error overlay — rendered inside the iframe via DOM so it doesn't
61+ // interfere with Sandpack's parent-frame message protocol.
62+ function showError ( message ) {
63+ var overlay = document . getElementById ( 'rsc-error-overlay' ) ;
64+ if ( ! overlay ) {
65+ overlay = document . createElement ( 'div' ) ;
66+ overlay . id = 'rsc-error-overlay' ;
67+ overlay . style . cssText =
68+ 'background:#fff;border:2px solid #c00;border-radius:8px;padding:16px;margin:20px;font-family:sans-serif;' ;
69+ var heading = document . createElement ( 'h2' ) ;
70+ heading . style . cssText = 'color:#c00;margin:0 0 8px;font-size:16px;' ;
71+ heading . textContent = 'Server Error' ;
72+ overlay . appendChild ( heading ) ;
73+ var pre = document . createElement ( 'pre' ) ;
74+ pre . style . cssText =
75+ 'margin:0;white-space:pre-wrap;word-break:break-word;font-size:14px;color:#222;' ;
76+ overlay . appendChild ( pre ) ;
77+ rootEl . parentNode . insertBefore ( overlay , rootEl ) ;
78+ }
79+ overlay . lastChild . textContent = message ;
80+ overlay . style . display = '' ;
81+ rootEl . style . display = 'none' ;
82+ }
83+
84+ function clearError ( ) {
85+ var overlay = document . getElementById ( 'rsc-error-overlay' ) ;
86+ if ( overlay ) overlay . style . display = 'none' ;
87+ rootEl . style . display = '' ;
88+ }
89+
6090 // Worker message handler
6191 worker . onmessage = function ( e ) {
6292 var msg = e . data ;
@@ -67,15 +97,19 @@ export function initClient() {
6797 pendingFiles = null ;
6898 }
6999 } else if ( msg . type === 'deploy-result' ) {
100+ clearError ( ) ;
70101 // Register compiled client modules in the webpack cache before rendering
71102 if ( msg . result && msg . result . compiledClients ) {
72- registerClientModules ( msg . result . compiledClients ) ;
103+ registerClientModules (
104+ msg . result . compiledClients ,
105+ msg . result . clientEntries || { }
106+ ) ;
73107 }
74108 triggerRender ( ) ;
75109 } else if ( msg . type === 'rsc-chunk' ) {
76110 handleChunk ( msg ) ;
77111 } else if ( msg . type === 'rsc-error' ) {
78- console . error ( 'RSC Worker error:' , msg . error ) ;
112+ showError ( msg . error ) ;
79113 }
80114 } ;
81115
@@ -184,37 +218,20 @@ export function initClient() {
184218
185219 function processFiles ( files ) {
186220 console . clear ( ) ;
187- var serverFiles = { } ;
188- var clientManifest = { } ;
189- var clientFiles = { } ;
190-
221+ var userFiles = { } ;
191222 Object . keys ( files ) . forEach ( function ( filePath ) {
192- var code = files [ filePath ] ;
193-
194- // Skip non-JS files and infrastructure files
195223 if ( ! filePath . match ( / \. ( j s | j s x | t s | t s x ) $ / ) ) return ;
196224 if ( filePath . indexOf ( 'node_modules' ) !== - 1 ) return ;
197225 if ( filePath === '/src/index.js' ) return ;
198226 if ( filePath === '/src/rsc-client.js' ) return ;
199227 if ( filePath === '/src/rsc-server.js' ) return ;
200-
201- // Check for 'use client' directive
202- if ( hasDirective ( code , 'use client' ) ) {
203- clientManifest [ filePath ] = true ;
204- clientFiles [ filePath ] = code ;
205- } else {
206- // Server file — send raw source to Worker for compilation
207- serverFiles [ filePath ] = code ;
208- }
228+ if ( filePath === '/src/__webpack_shim__.js' ) return ;
229+ userFiles [ filePath ] = files [ filePath ] ;
209230 } ) ;
210-
211- // Send raw server + client files to Worker (Worker compiles with Sucrase)
212231 worker . postMessage ( {
213232 type : 'deploy' ,
214233 requestId : ++ renderRequestId ,
215- serverFiles : serverFiles ,
216- clientManifest : clientManifest ,
217- clientFiles : clientFiles ,
234+ files : userFiles ,
218235 } ) ;
219236 }
220237
@@ -237,66 +254,64 @@ export function initClient() {
237254
238255 // Evaluate compiled client modules and register them in the webpack cache
239256 // so RSDW client can resolve them via __webpack_require__.
240- function registerClientModules ( compiledClients ) {
241- var moduleIds = Object . keys ( compiledClients ) ;
242- moduleIds . forEach ( function ( moduleId ) {
243- var code = compiledClients [ moduleId ] ;
257+ function registerClientModules ( compiledClients , clientEntries ) {
258+ // Store all compiled code for lazy evaluation
259+ var allCompiled = compiledClients ;
260+
261+ function evaluateModule ( moduleId ) {
262+ if ( globalThis . __webpack_module_cache__ [ moduleId ] ) {
263+ var cached = globalThis . __webpack_module_cache__ [ moduleId ] ;
264+ return cached . exports !== undefined ? cached . exports : cached ;
265+ }
266+ var code = allCompiled [ moduleId ] ;
267+ if ( ! code )
268+ throw new Error ( 'Client require: module "' + moduleId + '" not found' ) ;
269+
244270 var mod = { exports : { } } ;
271+ // Register before evaluating to handle circular deps
272+ globalThis . __webpack_module_cache__ [ moduleId ] = { exports : mod . exports } ;
273+
245274 var clientRequire = function ( id ) {
246275 if ( id === 'react' ) return React ;
247276 if ( id === 'react/jsx-runtime' ) return ReactJSXRuntime ;
248277 if ( id === 'react/jsx-dev-runtime' ) return ReactJSXDevRuntime ;
249278 if ( id . endsWith ( '.css' ) ) return { } ;
250279 var resolvedId = id . startsWith ( '.' ) ? resolvePath ( moduleId , id ) : id ;
251- // Try exact match, then with extensions
252280 var candidates = [ resolvedId ] ;
253281 var exts = [ '.js' , '.jsx' , '.ts' , '.tsx' ] ;
254282 for ( var i = 0 ; i < exts . length ; i ++ ) {
255283 candidates . push ( resolvedId + exts [ i ] ) ;
256284 }
257285 for ( var j = 0 ; j < candidates . length ; j ++ ) {
258- var cached = globalThis . __webpack_module_cache__ [ candidates [ j ] ] ;
259- if ( cached )
260- return cached . exports !== undefined ? cached . exports : cached ;
286+ if (
287+ allCompiled [ candidates [ j ] ] ||
288+ globalThis . __webpack_module_cache__ [ candidates [ j ] ]
289+ ) {
290+ return evaluateModule ( candidates [ j ] ) ;
291+ }
261292 }
262293 throw new Error ( 'Client require: module "' + id + '" not found' ) ;
263294 } ;
295+
264296 try {
265- new Function (
266- 'module' ,
267- 'exports' ,
268- 'require' ,
269- 'React' ,
270- code
271- ) ( mod , mod . exports , clientRequire , React ) ;
297+ new Function ( 'module' , 'exports' , 'require' , 'React' , code ) (
298+ mod ,
299+ mod . exports ,
300+ clientRequire ,
301+ React
302+ ) ;
272303 } catch ( err ) {
273304 console . error ( 'Error executing client module ' + moduleId + ':' , err ) ;
274- return ;
305+ return mod . exports ;
275306 }
307+ // Update cache with actual exports (handles non-circular case)
276308 globalThis . __webpack_module_cache__ [ moduleId ] = { exports : mod . exports } ;
277- } ) ;
278- }
279-
280- function hasDirective ( code , directive ) {
281- var lines = code . split ( '\n' ) ;
282- for ( var i = 0 ; i < Math . min ( lines . length , 10 ) ; i ++ ) {
283- var line = lines [ i ] . trim ( ) ;
284- if ( line === '' ) continue ;
285- if ( line . startsWith ( '//' ) ) continue ;
286- if ( line . startsWith ( '/*' ) ) {
287- while ( i < lines . length && ! lines [ i ] . includes ( '*/' ) ) i ++ ;
288- continue ;
289- }
290- if (
291- line === "'" + directive + "';" ||
292- line === '"' + directive + '";' ||
293- line === "'" + directive + "'" ||
294- line === '"' + directive + '"'
295- ) {
296- return true ;
297- }
298- return false ;
309+ return mod . exports ;
299310 }
300- return false ;
311+
312+ // Eagerly evaluate 'use client' entry points; their imports resolve lazily
313+ Object . keys ( clientEntries ) . forEach ( function ( moduleId ) {
314+ evaluateModule ( moduleId ) ;
315+ } ) ;
301316 }
302317}
0 commit comments