From 1713df13524ad20c6bd0413bcf4aa57adc3f9735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 31 Oct 2023 01:25:58 +0100 Subject: feat: deno run --unstable-hmr (#20876) This commit adds `--unstable-hmr` flag, that enabled Hot Module Replacement. This flag works like `--watch` and accepts the same arguments. If HMR is not possible the process will be restarted instead. Currently HMR is only supported in `deno run` subcommand. Upon HMR a `CustomEvent("hmr")` will be dispatched that contains information which file was changed in its `details` property. --------- Co-authored-by: Valentin Anger Co-authored-by: David Sherret --- cli/tests/integration/watcher_tests.rs | 254 +++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) (limited to 'cli/tests/integration') diff --git a/cli/tests/integration/watcher_tests.rs b/cli/tests/integration/watcher_tests.rs index 1ee8a45e0..0defaa69e 100644 --- a/cli/tests/integration/watcher_tests.rs +++ b/cli/tests/integration/watcher_tests.rs @@ -1645,3 +1645,257 @@ async fn run_watch_inspect() { check_alive_then_kill(child); } + +#[tokio::test] +async fn run_hmr_server() { + let t = TempDir::new(); + let file_to_watch = t.path().join("file_to_watch.js"); + file_to_watch.write( + r#" +globalThis.state = { i: 0 }; + +function bar() { + globalThis.state.i = 0; + console.log("got request", globalThis.state.i); +} + +function handler(_req) { + bar(); + return new Response("Hello world!"); +} + +Deno.serve({ port: 11111 }, handler); +console.log("Listening...") + "#, + ); + + let mut child = util::deno_cmd() + .current_dir(util::testdata_path()) + .arg("run") + .arg("--unstable-hmr") + .arg("--allow-net") + .arg("-L") + .arg("debug") + .arg(&file_to_watch) + .env("NO_COLOR", "1") + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .spawn() + .unwrap(); + let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); + wait_contains("Process started", &mut stderr_lines).await; + wait_contains("No package.json file found", &mut stderr_lines).await; + + wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; + wait_contains("Listening...", &mut stdout_lines).await; + + file_to_watch.write( + r#" +globalThis.state = { i: 0 }; + +function bar() { + globalThis.state.i = 0; + console.log("got request1", globalThis.state.i); +} + +function handler(_req) { + bar(); + return new Response("Hello world!"); +} + +Deno.serve({ port: 11111 }, handler); +console.log("Listening...") + "#, + ); + + wait_contains("Failed to reload module", &mut stderr_lines).await; + wait_contains("File change detected", &mut stderr_lines).await; + + check_alive_then_kill(child); +} + +#[tokio::test] +async fn run_hmr_jsx() { + let t = TempDir::new(); + let file_to_watch = t.path().join("file_to_watch.js"); + file_to_watch.write( + r#" +import { foo } from "./foo.jsx"; + +let i = 0; +setInterval(() => { + console.log(i++, foo()); +}, 100); +"#, + ); + let file_to_watch2 = t.path().join("foo.jsx"); + file_to_watch2.write( + r#" +export function foo() { + return `

Hello

`; +} +"#, + ); + + let mut child = util::deno_cmd() + .current_dir(util::testdata_path()) + .arg("run") + .arg("--unstable-hmr") + .arg("-L") + .arg("debug") + .arg(&file_to_watch) + .env("NO_COLOR", "1") + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .spawn() + .unwrap(); + let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); + wait_contains("Process started", &mut stderr_lines).await; + wait_contains("No package.json file found", &mut stderr_lines).await; + + wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; + wait_contains("5

Hello

", &mut stdout_lines).await; + + file_to_watch2.write( + r#" +export function foo() { + return `

Hello world

`; +} + "#, + ); + + wait_contains("Replaced changed module", &mut stderr_lines).await; + wait_contains("

Hello world

", &mut stdout_lines).await; + + check_alive_then_kill(child); +} + +#[tokio::test] +async fn run_hmr_uncaught_error() { + let t = TempDir::new(); + let file_to_watch = t.path().join("file_to_watch.js"); + file_to_watch.write( + r#" +import { foo } from "./foo.jsx"; + +let i = 0; +setInterval(() => { + console.log(i++, foo()); +}, 100); +"#, + ); + let file_to_watch2 = t.path().join("foo.jsx"); + file_to_watch2.write( + r#" +export function foo() { + setTimeout(() => { + throw new Error("fail"); + }); + return `

asd1

`; +} +"#, + ); + + let mut child = util::deno_cmd() + .current_dir(util::testdata_path()) + .arg("run") + .arg("--unstable-hmr") + .arg("-L") + .arg("debug") + .arg(&file_to_watch) + .env("NO_COLOR", "1") + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .spawn() + .unwrap(); + let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); + wait_contains("Process started", &mut stderr_lines).await; + wait_contains("No package.json file found", &mut stderr_lines).await; + + wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; + wait_contains("

asd1

", &mut stdout_lines).await; + wait_contains("fail", &mut stderr_lines).await; + + file_to_watch2.write( + r#" +export function foo() { + return `

asd2

`; +} + "#, + ); + + wait_contains("Process failed", &mut stderr_lines).await; + wait_contains("File change detected", &mut stderr_lines).await; + wait_contains("

asd2

", &mut stdout_lines).await; + + check_alive_then_kill(child); +} + +#[tokio::test] +async fn run_hmr_unhandled_rejection() { + let t = TempDir::new(); + let file_to_watch = t.path().join("file_to_watch.js"); + file_to_watch.write( + r#" +import { foo } from "./foo.jsx"; + +// deno-lint-ignore require-await +async function rejection() { + throw new Error("boom!"); +} + +let i = 0; +setInterval(() => { + if (i == 3) { + rejection(); + } + console.log(i++, foo()); +}, 100); +"#, + ); + let file_to_watch2 = t.path().join("foo.jsx"); + file_to_watch2.write( + r#" +export function foo() { + return `

asd1

`; +} +"#, + ); + + let mut child = util::deno_cmd() + .current_dir(util::testdata_path()) + .arg("run") + .arg("--unstable-hmr") + .arg("-L") + .arg("debug") + .arg(&file_to_watch) + .env("NO_COLOR", "1") + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .spawn() + .unwrap(); + let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); + wait_contains("Process started", &mut stderr_lines).await; + wait_contains("No package.json file found", &mut stderr_lines).await; + + wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; + wait_contains("2

asd1

", &mut stdout_lines).await; + wait_contains("boom", &mut stderr_lines).await; + + file_to_watch.write( + r#" +import { foo } from "./foo.jsx"; + +let i = 0; +setInterval(() => { + console.log(i++, foo()); +}, 100); + "#, + ); + + wait_contains("Process failed", &mut stderr_lines).await; + wait_contains("File change detected", &mut stderr_lines).await; + wait_contains("

asd1

", &mut stdout_lines).await; + + check_alive_then_kill(child); +} -- cgit v1.2.3