Skip to content
Merged
Show file tree
Hide file tree
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
12 changes: 10 additions & 2 deletions cli/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,16 @@ impl CliMainWorker {
/// Execute the given main module emitting load and unload events before and after execution
/// respectively.
pub async fn execute(&mut self) -> Result<(), CoreError> {
self.inner.execute_main_module().await?;
self.inner.worker.dispatch_load_event()?;
// Set pending_unload before module execution so that if the future
// is cancelled during a top-level await, Drop will still dispatch
// the unload event for any handlers registered during partial
// module evaluation.
self.pending_unload = true;
if let Err(e) = self.inner.execute_main_module().await {
self.pending_unload = false;
return Err(e);
}
self.inner.worker.dispatch_load_event()?;

let result = loop {
match self.inner.worker.run_event_loop(false).await {
Expand Down Expand Up @@ -226,6 +233,7 @@ impl CliMainWorker {
fn drop(&mut self) {
if self.pending_unload {
let _ = self.inner.worker.dispatch_unload_event();
let _ = self.inner.worker.dispatch_process_exit_event();
}
}
}
Expand Down
97 changes: 97 additions & 0 deletions tests/integration/watcher_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1347,6 +1347,103 @@ async fn test_watch_sigint() {
assert_eq!(exit_status.code(), Some(130));
}

/// Test that the "unload" event fires on watch restart when the
/// event loop is running (e.g. with setInterval keeping it alive).
#[test(flaky)]
async fn run_watch_unload_on_restart() {
let t = TempDir::new();
let file_to_watch = t.path().join("file_to_watch.js");
file_to_watch.write(
r#"
addEventListener("unload", () => {
console.log("unload event fired");
});
setInterval(() => {}, 1000);
"#,
);

let mut child = util::deno_cmd()
.current_dir(t.path())
.arg("run")
.arg("--watch")
.arg("-L")
.arg("debug")
.arg("--allow-all")
.arg(&file_to_watch)
.env("NO_COLOR", "1")
.piped_output()
.spawn()
.unwrap();
let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);

wait_for_watcher("file_to_watch.js", &mut stderr_lines).await;

// Trigger a restart by modifying the file
file_to_watch.write(
r#"
addEventListener("unload", () => {
console.log("unload event fired");
});
setInterval(() => {}, 1000);
// changed
"#,
);

// The unload handler should fire before the process restarts
wait_contains("unload event fired", &mut stdout_lines).await;
wait_contains("Restarting", &mut stderr_lines).await;
check_alive_then_kill(child);
}

/// Test that Node.js process "exit" event fires on watch restart.
#[test(flaky)]
async fn run_watch_process_exit_on_restart() {
let t = TempDir::new();
let file_to_watch = t.path().join("file_to_watch.js");
file_to_watch.write(
r#"
import process from "node:process";
process.on("exit", () => {
console.log("process exit fired");
});
setInterval(() => {}, 1000);
"#,
);

let mut child = util::deno_cmd()
.current_dir(t.path())
.arg("run")
.arg("--watch")
.arg("-L")
.arg("debug")
.arg("--allow-all")
.arg(&file_to_watch)
.env("NO_COLOR", "1")
.piped_output()
.spawn()
.unwrap();
let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);

wait_for_watcher("file_to_watch.js", &mut stderr_lines).await;

// Trigger a restart by modifying the file
file_to_watch.write(
r#"
import process from "node:process";
process.on("exit", () => {
console.log("process exit fired");
});
setInterval(() => {}, 1000);
// changed
"#,
);

// The process exit handler should fire before the process restarts
wait_contains("process exit fired", &mut stdout_lines).await;
wait_contains("Restarting", &mut stderr_lines).await;
check_alive_then_kill(child);
}

#[test(flaky)]
async fn bench_watch_basic() {
let t = TempDir::new();
Expand Down
Loading