@@ -23,16 +23,20 @@ import { SandpackClient } from "../base";
2323
2424import Protocol from "./file-resolver-protocol" ;
2525import { IFrameProtocol } from "./iframe-protocol" ;
26- import type { SandpackRuntimeMessage } from "./types" ;
27- import { getTemplate } from "./utils" ;
26+ import { EXTENSIONS_MAP } from "./mime" ;
27+ import type { IPreviewRequestMessage , IPreviewResponseMessage } from "./types" ;
28+ import { CHANNEL_NAME , type SandpackRuntimeMessage } from "./types" ;
29+ import { getExtension , getTemplate } from "./utils" ;
30+
31+ const SUFFIX_PLACEHOLDER = "-{{suffix}}" ;
2832
2933const BUNDLER_URL =
3034 process . env . CODESANDBOX_ENV === "development"
3135 ? "http://localhost:3000/"
3236 : `https://${ process . env . PACKAGE_VERSION ?. replace (
3337 / \. / g,
3438 "-"
35- ) } -sandpack.codesandbox.io/`;
39+ ) } ${ SUFFIX_PLACEHOLDER } -sandpack.codesandbox.io/`;
3640
3741export class SandpackRuntime extends SandpackClient {
3842 fileResolverProtocol ?: Protocol ;
@@ -62,6 +66,15 @@ export class SandpackRuntime extends SandpackClient {
6266 `?cache=${ Date . now ( ) } ` ;
6367 }
6468
69+ const suffixes : string [ ] = [ ] ;
70+ if ( options . experimental_enableServiceWorker ) {
71+ suffixes . push ( Math . random ( ) . toString ( 36 ) . slice ( 4 ) ) ;
72+ }
73+ this . bundlerURL = this . bundlerURL . replace (
74+ SUFFIX_PLACEHOLDER ,
75+ suffixes . length ? `-${ suffixes . join ( "-" ) } ` : ""
76+ ) ;
77+
6578 this . bundlerState = undefined ;
6679 this . errors = [ ] ;
6780 this . status = "initializing" ;
@@ -153,6 +166,89 @@ export class SandpackRuntime extends SandpackClient {
153166 }
154167 }
155168 ) ;
169+
170+ if ( options . experimental_enableServiceWorker ) {
171+ this . serviceWorkerHandshake ( ) ;
172+ }
173+ }
174+
175+ private serviceWorkerHandshake ( ) {
176+ const channel = new MessageChannel ( ) ;
177+
178+ const iframeContentWindow = this . iframe . contentWindow ;
179+ if ( ! iframeContentWindow ) {
180+ throw new Error ( "Could not get iframe contentWindow" ) ;
181+ }
182+
183+ const port = channel . port1 ;
184+ port . onmessage = ( evt : MessageEvent ) => {
185+ if ( typeof evt . data === "object" && evt . data . $channel === CHANNEL_NAME ) {
186+ switch ( evt . data . $type ) {
187+ case "preview/ready" :
188+ // no op for now
189+ break ;
190+ case "preview/request" :
191+ this . handleWorkerRequest ( evt . data , port ) ;
192+
193+ break ;
194+ }
195+ }
196+ } ;
197+
198+ this . iframe . onload = ( ) => {
199+ const initMsg = {
200+ $channel : CHANNEL_NAME ,
201+ $type : "preview/init" ,
202+ } ;
203+
204+ iframeContentWindow . postMessage ( initMsg , "*" , [ channel . port2 ] ) ;
205+ } ;
206+ }
207+
208+ private handleWorkerRequest (
209+ request : IPreviewRequestMessage ,
210+ port : MessagePort
211+ ) {
212+ try {
213+ const filepath = new URL ( request . url , this . bundlerURL ) . pathname ;
214+
215+ const headers : Record < string , string > = { } ;
216+
217+ const files = this . getFiles ( ) ;
218+ const body = files [ filepath ] . code ;
219+
220+ if ( ! headers [ "Content-Type" ] ) {
221+ const extension = getExtension ( filepath ) ;
222+ const foundMimetype = EXTENSIONS_MAP . get ( extension ) ;
223+ if ( foundMimetype ) {
224+ headers [ "Content-Type" ] = foundMimetype ;
225+ }
226+ }
227+
228+ const responseMessage : IPreviewResponseMessage = {
229+ $channel : CHANNEL_NAME ,
230+ $type : "preview/response" ,
231+ id : request . id ,
232+ headers,
233+ status : 200 ,
234+ body,
235+ } ;
236+
237+ port . postMessage ( responseMessage ) ;
238+ } catch ( err ) {
239+ const responseMessage : IPreviewResponseMessage = {
240+ $channel : CHANNEL_NAME ,
241+ $type : "preview/response" ,
242+ id : request . id ,
243+ headers : {
244+ "Content-Type" : "text/html; charset=utf-8" ,
245+ } ,
246+ status : 404 ,
247+ body : "File not found" ,
248+ } ;
249+
250+ port . postMessage ( responseMessage ) ;
251+ }
156252 }
157253
158254 public setLocationURLIntoIFrame ( ) : void {
@@ -240,6 +336,8 @@ export class SandpackRuntime extends SandpackClient {
240336 hasFileResolver : Boolean ( this . options . fileResolver ) ,
241337 disableDependencyPreprocessing :
242338 this . sandboxSetup . disableDependencyPreprocessing ,
339+ experimental_enableServiceWorker :
340+ this . options . experimental_enableServiceWorker ,
243341 template :
244342 this . sandboxSetup . template ||
245343 getTemplate ( packageJSON , normalizedModules ) ,
0 commit comments