@@ -4,6 +4,7 @@ import AnsiToHtml from 'ansi-to-html';
44import * as yootils from 'yootils' ;
55import { escape_html , get_depth } from '../../../utils.js' ;
66import { ready } from '../common/index.js' ;
7+ import { isWebContainerSupported } from './utils.js' ;
78
89/**
910 * @typedef {import("../../../../routes/tutorial/[slug]/state.js").CompilerWarning } CompilerWarning
@@ -25,13 +26,15 @@ let vm;
2526 * @returns {Promise<import('$lib/types').Adapter> }
2627 */
2728export async function create ( base , error , progress , logs , warnings ) {
28- if ( / s a f a r i / i . test ( navigator . userAgent ) && ! / c h r o m e / i . test ( navigator . userAgent ) ) {
29- throw new Error ( 'WebContainers are not supported by Safari' ) ;
29+ if ( ! isWebContainerSupported ( ) ) {
30+ throw new Error ( 'WebContainers are not supported by Safari 16.3 or earlier ' ) ;
3031 }
3132
3233 progress . set ( { value : 0 , text : 'loading files' } ) ;
3334
3435 const q = yootils . queue ( 1 ) ;
36+ /** @type {Map<string, Array<import('$lib/types').FileStub>> } */
37+ const q_per_file = new Map ( ) ;
3538
3639 /** Paths and contents of the currently loaded file stubs */
3740 let current_stubs = stubs_to_map ( [ ] ) ;
@@ -203,25 +206,9 @@ export async function create(base, error, progress, logs, warnings) {
203206 // For some reason, server-ready is fired again when the vite dev server is restarted.
204207 // We need to wait for it to finish before we can continue, else we might
205208 // request files from Vite before it's ready, leading to a timeout.
206- const will_restart = launched && to_write . some ( will_restart_vite_dev_server ) ;
207- const promise = will_restart
208- ? new Promise ( ( fulfil , reject ) => {
209- const error_unsub = vm . on ( 'error' , ( error ) => {
210- error_unsub ( ) ;
211- reject ( new Error ( error . message ) ) ;
212- } ) ;
213-
214- const ready_unsub = vm . on ( 'server-ready' , ( port , base ) => {
215- ready_unsub ( ) ;
216- console . log ( `server ready on port ${ port } at ${ performance . now ( ) } : ${ base } ` ) ;
217- fulfil ( undefined ) ;
218- } ) ;
219-
220- setTimeout ( ( ) => {
221- reject ( new Error ( 'Timed out resetting WebContainer' ) ) ;
222- } , 10000 ) ;
223- } )
224- : Promise . resolve ( ) ;
209+ const will_restart = launched &&
210+ ( to_write . some ( is_config ) || to_delete . some ( is_config_path ) ) ;
211+ const promise = will_restart ? wait_for_restart_vite ( ) : Promise . resolve ( ) ;
225212
226213 for ( const file of to_delete ) {
227214 await vm . fs . rm ( file , { force : true , recursive : true } ) ;
@@ -241,6 +228,15 @@ export async function create(base, error, progress, logs, warnings) {
241228 } ) ;
242229 } ,
243230 update : ( file ) => {
231+
232+ let queue = q_per_file . get ( file . name ) ;
233+ if ( queue ) {
234+ queue . push ( file ) ;
235+ return Promise . resolve ( false ) ;
236+ }
237+
238+ q_per_file . set ( file . name , queue = [ file ] ) ;
239+
244240 return q . add ( async ( ) => {
245241 /** @type {import('@webcontainer/api').FileSystemTree } */
246242 const root = { } ;
@@ -263,22 +259,35 @@ export async function create(base, error, progress, logs, warnings) {
263259 tree = /** @type {import('@webcontainer/api').DirectoryNode } */ ( tree [ part ] ) . directory ;
264260 }
265261
266- tree [ basename ] = to_file ( file ) ;
262+ const will_restart = is_config ( file ) ;
263+
264+ while ( queue && queue . length > 0 ) {
267265
268- // initialize warnings of this file
269- $warnings [ file . name ] = [ ] ;
270- schedule_to_update_warning ( 100 ) ;
266+ // if the file is updated many times rapidly, get the most recently updated one
267+ const file = /** @type { import('$lib/types').FileStub } */ ( queue . pop ( ) ) ;
268+ queue . length = 0
271269
272- await vm . mount ( root ) ;
270+ tree [ basename ] = to_file ( file ) ;
273271
274- current_stubs . set ( file . name , file ) ;
272+ // initialize warnings of this file
273+ $warnings [ file . name ] = [ ] ;
274+ schedule_to_update_warning ( 100 ) ;
275+
276+ await vm . mount ( root ) ;
277+
278+ if ( will_restart ) await wait_for_restart_vite ( ) ;
279+
280+ current_stubs . set ( file . name , file ) ;
281+
282+ // we need to stagger sequential updates, just enough that the HMR
283+ // wires don't get crossed. 50ms seems to be enough of a delay
284+ // to avoid glitches without noticeably affecting update speed
285+ await new Promise ( ( f ) => setTimeout ( f , 50 ) ) ;
286+ }
275287
276- // we need to stagger sequential updates, just enough that the HMR
277- // wires don't get crossed. 50ms seems to be enough of a delay
278- // to avoid glitches without noticeably affecting update speed
279- await new Promise ( ( f ) => setTimeout ( f , 50 ) ) ;
288+ q_per_file . delete ( file . name )
280289
281- return will_restart_vite_dev_server ( file ) ;
290+ return will_restart ;
282291 } ) ;
283292 }
284293 } ;
@@ -287,11 +296,34 @@ export async function create(base, error, progress, logs, warnings) {
287296/**
288297 * @param {import('$lib/types').Stub } file
289298 */
290- function will_restart_vite_dev_server ( file ) {
291- return (
292- file . type === 'file' &&
293- ( file . name === '/vite.config.js' || file . name === '/svelte.config.js' || file . name === '/.env' )
294- ) ;
299+ function is_config ( file ) {
300+ return file . type === 'file' && is_config_path ( file . name ) ;
301+ }
302+
303+ /**
304+ * @param {string } path
305+ */
306+ function is_config_path ( path ) {
307+ return [ '/vite.config.js' , '/svelte.config.js' , '/.env' ] . includes ( path ) ;
308+ }
309+
310+ function wait_for_restart_vite ( ) {
311+ return new Promise ( ( fulfil , reject ) => {
312+ const error_unsub = vm . on ( 'error' , ( error ) => {
313+ error_unsub ( ) ;
314+ reject ( new Error ( error . message ) ) ;
315+ } ) ;
316+
317+ const ready_unsub = vm . on ( 'server-ready' , ( port , base ) => {
318+ ready_unsub ( ) ;
319+ console . log ( `server ready on port ${ port } at ${ performance . now ( ) } : ${ base } ` ) ;
320+ fulfil ( undefined ) ;
321+ } ) ;
322+
323+ setTimeout ( ( ) => {
324+ reject ( new Error ( 'Timed out resetting WebContainer' ) ) ;
325+ } , 10000 ) ;
326+ } ) ;
295327}
296328
297329/**
0 commit comments