@@ -32,6 +32,8 @@ export async function create(base, error, progress, logs, warnings) {
3232 progress . set ( { value : 0 , text : 'loading files' } ) ;
3333
3434 const q = yootils . queue ( 1 ) ;
35+ /** @type {Map<string, Array<import('$lib/types').FileStub>> } */
36+ const q_per_file = new Map ( ) ;
3537
3638 /** Paths and contents of the currently loaded file stubs */
3739 let current_stubs = stubs_to_map ( [ ] ) ;
@@ -203,25 +205,9 @@ export async function create(base, error, progress, logs, warnings) {
203205 // For some reason, server-ready is fired again when the vite dev server is restarted.
204206 // We need to wait for it to finish before we can continue, else we might
205207 // 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 ( ) ;
208+ const will_restart = launched &&
209+ ( to_write . some ( is_config ) || to_delete . some ( is_config_path ) ) ;
210+ const promise = will_restart ? wait_for_restart_vite ( ) : Promise . resolve ( ) ;
225211
226212 for ( const file of to_delete ) {
227213 await vm . fs . rm ( file , { force : true , recursive : true } ) ;
@@ -241,6 +227,15 @@ export async function create(base, error, progress, logs, warnings) {
241227 } ) ;
242228 } ,
243229 update : ( file ) => {
230+
231+ let queue = q_per_file . get ( file . name ) ;
232+ if ( queue ) {
233+ queue . push ( file ) ;
234+ return Promise . resolve ( false ) ;
235+ }
236+
237+ q_per_file . set ( file . name , queue = [ file ] ) ;
238+
244239 return q . add ( async ( ) => {
245240 /** @type {import('@webcontainer/api').FileSystemTree } */
246241 const root = { } ;
@@ -263,22 +258,35 @@ export async function create(base, error, progress, logs, warnings) {
263258 tree = /** @type {import('@webcontainer/api').DirectoryNode } */ ( tree [ part ] ) . directory ;
264259 }
265260
266- tree [ basename ] = to_file ( file ) ;
261+ const will_restart = is_config ( file ) ;
262+
263+ while ( queue && queue . length > 0 ) {
267264
268- // initialize warnings of this file
269- $warnings [ file . name ] = [ ] ;
270- schedule_to_update_warning ( 100 ) ;
265+ // if the file is updated many times rapidly, get the most recently updated one
266+ const file = /** @type { import('$lib/types').FileStub } */ ( queue . pop ( ) ) ;
267+ queue . length = 0
271268
272- await vm . mount ( root ) ;
269+ tree [ basename ] = to_file ( file ) ;
273270
274- current_stubs . set ( file . name , file ) ;
271+ // initialize warnings of this file
272+ $warnings [ file . name ] = [ ] ;
273+ schedule_to_update_warning ( 100 ) ;
274+
275+ await vm . mount ( root ) ;
276+
277+ if ( will_restart ) await wait_for_restart_vite ( ) ;
278+
279+ current_stubs . set ( file . name , file ) ;
280+
281+ // we need to stagger sequential updates, just enough that the HMR
282+ // wires don't get crossed. 50ms seems to be enough of a delay
283+ // to avoid glitches without noticeably affecting update speed
284+ await new Promise ( ( f ) => setTimeout ( f , 50 ) ) ;
285+ }
275286
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 ) ) ;
287+ q_per_file . delete ( file . name )
280288
281- return will_restart_vite_dev_server ( file ) ;
289+ return will_restart ;
282290 } ) ;
283291 }
284292 } ;
@@ -287,11 +295,34 @@ export async function create(base, error, progress, logs, warnings) {
287295/**
288296 * @param {import('$lib/types').Stub } file
289297 */
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- ) ;
298+ function is_config ( file ) {
299+ return file . type === 'file' && is_config_path ( file . name ) ;
300+ }
301+
302+ /**
303+ * @param {string } path
304+ */
305+ function is_config_path ( path ) {
306+ return [ '/vite.config.js' , '/svelte.config.js' , '/.env' ] . includes ( path ) ;
307+ }
308+
309+ function wait_for_restart_vite ( ) {
310+ return new Promise ( ( fulfil , reject ) => {
311+ const error_unsub = vm . on ( 'error' , ( error ) => {
312+ error_unsub ( ) ;
313+ reject ( new Error ( error . message ) ) ;
314+ } ) ;
315+
316+ const ready_unsub = vm . on ( 'server-ready' , ( port , base ) => {
317+ ready_unsub ( ) ;
318+ console . log ( `server ready on port ${ port } at ${ performance . now ( ) } : ${ base } ` ) ;
319+ fulfil ( undefined ) ;
320+ } ) ;
321+
322+ setTimeout ( ( ) => {
323+ reject ( new Error ( 'Timed out resetting WebContainer' ) ) ;
324+ } , 10000 ) ;
325+ } ) ;
295326}
296327
297328/**
0 commit comments