From 1587d297bee26fe2da177a267b14cf5c8adc8173 Mon Sep 17 00:00:00 2001 From: tomoam <29677552+tomoam@users.noreply.github.com> Date: Thu, 13 Apr 2023 21:16:55 +0900 Subject: [PATCH 1/2] fix: correct behaviour when .env file is updated or removed --- src/lib/client/adapters/webcontainer/index.js | 61 +++++++++++-------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/src/lib/client/adapters/webcontainer/index.js b/src/lib/client/adapters/webcontainer/index.js index 701ad8aca..7946500ce 100644 --- a/src/lib/client/adapters/webcontainer/index.js +++ b/src/lib/client/adapters/webcontainer/index.js @@ -159,25 +159,9 @@ export async function create(base, error, progress, logs) { // For some reason, server-ready is fired again when the vite dev server is restarted. // We need to wait for it to finish before we can continue, else we might // request files from Vite before it's ready, leading to a timeout. - const will_restart = launched && to_write.some(will_restart_vite_dev_server); - const promise = will_restart - ? new Promise((fulfil, reject) => { - const error_unsub = vm.on('error', (error) => { - error_unsub(); - reject(new Error(error.message)); - }); - - const ready_unsub = vm.on('server-ready', (port, base) => { - ready_unsub(); - console.log(`server ready on port ${port} at ${performance.now()}: ${base}`); - fulfil(undefined); - }); - - setTimeout(() => { - reject(new Error('Timed out resetting WebContainer')); - }, 10000); - }) - : Promise.resolve(); + const will_restart = launched && + (to_write.some(is_config) || to_delete.some(is_config_path)); + const promise = will_restart ? wait_for_restart_vite() : Promise.resolve(); for (const file of to_delete) { await vm.fs.rm(file, { force: true, recursive: true }); @@ -219,9 +203,13 @@ export async function create(base, error, progress, logs) { tree = /** @type {import('@webcontainer/api').DirectoryNode} */ (tree[part]).directory; } + const will_restart = is_config(file); + const promise = will_restart ? wait_for_restart_vite() : Promise.resolve(); + tree[basename] = to_file(file); await vm.mount(root); + await promise; current_stubs.set(file.name, file); @@ -230,7 +218,7 @@ export async function create(base, error, progress, logs) { // to avoid glitches without noticeably affecting update speed await new Promise((f) => setTimeout(f, 50)); - return will_restart_vite_dev_server(file); + return will_restart; }); } }; @@ -239,11 +227,34 @@ export async function create(base, error, progress, logs) { /** * @param {import('$lib/types').Stub} file */ -function will_restart_vite_dev_server(file) { - return ( - file.type === 'file' && - (file.name === '/vite.config.js' || file.name === '/svelte.config.js' || file.name === '/.env') - ); +function is_config(file) { + return file.type === 'file' && is_config_path(file.name); +} + +/** + * @param {string} path + */ +function is_config_path(path) { + return ['/vite.config.js', '/svelte.config.js', '/.env'].includes(path); +} + +function wait_for_restart_vite() { + return new Promise((fulfil, reject) => { + const error_unsub = vm.on('error', (error) => { + error_unsub(); + reject(new Error(error.message)); + }); + + const ready_unsub = vm.on('server-ready', (port, base) => { + ready_unsub(); + console.log(`server ready on port ${port} at ${performance.now()}: ${base}`); + fulfil(undefined); + }); + + setTimeout(() => { + reject(new Error('Timed out resetting WebContainer')); + }, 10000); + }); } /** From a4662e396fd787f1ccf046b10bc11791805aa5a2 Mon Sep 17 00:00:00 2001 From: tomoam <29677552+tomoam@users.noreply.github.com> Date: Sun, 16 Apr 2023 20:42:40 +0900 Subject: [PATCH 2/2] add mechanism like debounce --- src/lib/client/adapters/webcontainer/index.js | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/lib/client/adapters/webcontainer/index.js b/src/lib/client/adapters/webcontainer/index.js index 7946500ce..9b76fce04 100644 --- a/src/lib/client/adapters/webcontainer/index.js +++ b/src/lib/client/adapters/webcontainer/index.js @@ -27,6 +27,8 @@ export async function create(base, error, progress, logs) { progress.set({ value: 0, text: 'loading files' }); const q = yootils.queue(1); + /** @type {Map>} */ + const q_per_file = new Map(); /** Paths and contents of the currently loaded file stubs */ let current_stubs = stubs_to_map([]); @@ -181,6 +183,15 @@ export async function create(base, error, progress, logs) { }); }, update: (file) => { + + let queue = q_per_file.get(file.name); + if (queue) { + queue.push(file); + return Promise.resolve(false); + } + + q_per_file.set(file.name, queue = [file]); + return q.add(async () => { /** @type {import('@webcontainer/api').FileSystemTree} */ const root = {}; @@ -204,19 +215,28 @@ export async function create(base, error, progress, logs) { } const will_restart = is_config(file); - const promise = will_restart ? wait_for_restart_vite() : Promise.resolve(); - tree[basename] = to_file(file); + while (queue && queue.length > 0) { - await vm.mount(root); - await promise; + // if the file is updated many times rapidly, get the most recently updated one + const file = /** @type {import('$lib/types').FileStub} */ (queue.pop()); + queue.length = 0 - current_stubs.set(file.name, file); + tree[basename] = to_file(file); + + await vm.mount(root); + + if (will_restart) await wait_for_restart_vite(); + + current_stubs.set(file.name, file); + + // we need to stagger sequential updates, just enough that the HMR + // wires don't get crossed. 50ms seems to be enough of a delay + // to avoid glitches without noticeably affecting update speed + await new Promise((f) => setTimeout(f, 50)); + } - // we need to stagger sequential updates, just enough that the HMR - // wires don't get crossed. 50ms seems to be enough of a delay - // to avoid glitches without noticeably affecting update speed - await new Promise((f) => setTimeout(f, 50)); + q_per_file.delete(file.name) return will_restart; });