Skip to content

fix: correct behaviour when .env file is updated or removed #363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 11, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 66 additions & 35 deletions src/lib/client/adapters/webcontainer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export async function create(base, error, progress, logs, warnings) {
progress.set({ value: 0, text: 'loading files' });

const q = yootils.queue(1);
/** @type {Map<string, Array<import('$lib/types').FileStub>>} */
const q_per_file = new Map();

/** Paths and contents of the currently loaded file stubs */
let current_stubs = stubs_to_map([]);
Expand Down Expand Up @@ -203,25 +205,9 @@ export async function create(base, error, progress, logs, warnings) {
// 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 });
Expand All @@ -241,6 +227,15 @@ export async function create(base, error, progress, logs, warnings) {
});
},
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 = {};
Expand All @@ -263,22 +258,35 @@ export async function create(base, error, progress, logs, warnings) {
tree = /** @type {import('@webcontainer/api').DirectoryNode} */ (tree[part]).directory;
}

tree[basename] = to_file(file);
const will_restart = is_config(file);

while (queue && queue.length > 0) {

// initialize warnings of this file
$warnings[file.name] = [];
schedule_to_update_warning(100);
// 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

await vm.mount(root);
tree[basename] = to_file(file);

current_stubs.set(file.name, file);
// initialize warnings of this file
$warnings[file.name] = [];
schedule_to_update_warning(100);

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_vite_dev_server(file);
return will_restart;
});
}
};
Expand All @@ -287,11 +295,34 @@ export async function create(base, error, progress, logs, warnings) {
/**
* @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);
});
}

/**
Expand Down