summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/file_watcher.rs129
-rw-r--r--cli/main.rs111
-rw-r--r--cli/proc_state.rs78
-rw-r--r--cli/tests/integration/watcher_tests.rs259
4 files changed, 378 insertions, 199 deletions
diff --git a/cli/file_watcher.rs b/cli/file_watcher.rs
index 3c74237ca..36b4276e7 100644
--- a/cli/file_watcher.rs
+++ b/cli/file_watcher.rs
@@ -19,6 +19,7 @@ use std::sync::Arc;
use std::time::Duration;
use tokio::select;
use tokio::sync::mpsc;
+use tokio::sync::mpsc::UnboundedReceiver;
use tokio::time::sleep;
const CLEAR_SCREEN: &str = "\x1B[2J\x1B[1;1H";
@@ -114,6 +115,18 @@ pub struct PrintConfig {
pub clear_screen: bool,
}
+fn create_print_after_restart_fn(clear_screen: bool) -> impl Fn() {
+ move || {
+ if clear_screen {
+ eprint!("{}", CLEAR_SCREEN);
+ }
+ info!(
+ "{} File change detected! Restarting!",
+ colors::intense_blue("Watcher"),
+ );
+ }
+}
+
/// Creates a file watcher, which will call `resolver` with every file change.
///
/// - `resolver` is used for resolving file paths to be watched at every restarting
@@ -147,15 +160,7 @@ where
let mut paths_to_watch;
let mut resolution_result;
- let print_after_restart = || {
- if clear_screen {
- eprint!("{}", CLEAR_SCREEN);
- }
- info!(
- "{} File change detected! Restarting!",
- colors::intense_blue("Watcher"),
- );
- };
+ let print_after_restart = create_print_after_restart_fn(clear_screen);
match resolver(None).await {
ResolutionResult::Ignore => {
@@ -188,7 +193,8 @@ where
info!("{} {} started.", colors::intense_blue("Watcher"), job_name,);
loop {
- let watcher = new_watcher(&paths_to_watch, sender.clone())?;
+ let mut watcher = new_watcher(sender.clone())?;
+ add_paths_to_watcher(&mut watcher, &paths_to_watch);
match resolution_result {
Ok(operation_arg) => {
@@ -234,8 +240,99 @@ where
}
}
+/// Creates a file watcher.
+///
+/// - `operation` is the actual operation we want to run every time the watcher detects file
+/// changes. For example, in the case where we would like to bundle, then `operation` would
+/// have the logic for it like bundling the code.
+pub async fn watch_func2<T: Clone, O, F>(
+ mut paths_to_watch_receiver: mpsc::UnboundedReceiver<Vec<PathBuf>>,
+ mut operation: O,
+ operation_args: T,
+ print_config: PrintConfig,
+) -> Result<(), AnyError>
+where
+ O: FnMut(T) -> F,
+ F: Future<Output = Result<(), AnyError>>,
+{
+ let (watcher_sender, mut watcher_receiver) =
+ DebouncedReceiver::new_with_sender();
+
+ let PrintConfig {
+ job_name,
+ clear_screen,
+ } = print_config;
+
+ let print_after_restart = create_print_after_restart_fn(clear_screen);
+
+ info!("{} {} started.", colors::intense_blue("Watcher"), job_name,);
+
+ fn consume_paths_to_watch(
+ watcher: &mut RecommendedWatcher,
+ receiver: &mut UnboundedReceiver<Vec<PathBuf>>,
+ ) {
+ loop {
+ match receiver.try_recv() {
+ Ok(paths) => {
+ add_paths_to_watcher(watcher, &paths);
+ }
+ Err(e) => match e {
+ mpsc::error::TryRecvError::Empty => {
+ break;
+ }
+ // there must be at least one receiver alive
+ _ => unreachable!(),
+ },
+ }
+ }
+ }
+
+ loop {
+ let mut watcher = new_watcher(watcher_sender.clone())?;
+ consume_paths_to_watch(&mut watcher, &mut paths_to_watch_receiver);
+
+ let receiver_future = async {
+ loop {
+ let maybe_paths = paths_to_watch_receiver.recv().await;
+ add_paths_to_watcher(&mut watcher, &maybe_paths.unwrap());
+ }
+ };
+ let operation_future = error_handler(operation(operation_args.clone()));
+
+ select! {
+ _ = receiver_future => {},
+ _ = watcher_receiver.recv() => {
+ print_after_restart();
+ continue;
+ },
+ _ = operation_future => {
+ // TODO(bartlomieju): print exit code here?
+ info!(
+ "{} {} finished. Restarting on file change...",
+ colors::intense_blue("Watcher"),
+ job_name,
+ );
+ consume_paths_to_watch(&mut watcher, &mut paths_to_watch_receiver);
+ },
+ };
+
+ let receiver_future = async {
+ loop {
+ let maybe_paths = paths_to_watch_receiver.recv().await;
+ add_paths_to_watcher(&mut watcher, &maybe_paths.unwrap());
+ }
+ };
+ select! {
+ _ = receiver_future => {},
+ _ = watcher_receiver.recv() => {
+ print_after_restart();
+ continue;
+ },
+ };
+ }
+}
+
fn new_watcher(
- paths: &[PathBuf],
sender: Arc<mpsc::UnboundedSender<Vec<PathBuf>>>,
) -> Result<RecommendedWatcher, AnyError> {
let mut watcher: RecommendedWatcher =
@@ -257,11 +354,13 @@ fn new_watcher(
watcher.configure(Config::PreciseEvents(true)).unwrap();
- log::debug!("Watching paths: {:?}", paths);
+ Ok(watcher)
+}
+
+fn add_paths_to_watcher(watcher: &mut RecommendedWatcher, paths: &[PathBuf]) {
+ // Ignore any error e.g. `PathNotFound`
for path in paths {
- // Ignore any error e.g. `PathNotFound`
let _ = watcher.watch(path, RecursiveMode::Recursive);
}
-
- Ok(watcher)
+ log::debug!("Watching paths: {:?}", paths);
}
diff --git a/cli/main.rs b/cli/main.rs
index b6c7e97ff..f9f92359c 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -995,100 +995,6 @@ async fn run_from_stdin(flags: Flags) -> Result<i32, AnyError> {
// TODO(bartlomieju): this function is not handling `exit_code` set by the runtime
// code properly.
async fn run_with_watch(flags: Flags, script: String) -> Result<i32, AnyError> {
- let flags = Arc::new(flags);
- let resolver = |_| {
- let script1 = script.clone();
- let script2 = script.clone();
- let flags = flags.clone();
- let watch_flag = flags.watch.clone();
- async move {
- let main_module = resolve_url_or_path(&script1)?;
- let ps = ProcState::build(flags).await?;
- let mut cache = cache::FetchCacher::new(
- ps.dir.gen_cache.clone(),
- ps.file_fetcher.clone(),
- Permissions::allow_all(),
- Permissions::allow_all(),
- );
- let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
- let maybe_imports = if let Some(config_file) = &ps.maybe_config_file {
- config_file.to_maybe_imports()?
- } else {
- None
- };
- let maybe_import_map_resolver =
- ps.maybe_import_map.clone().map(ImportMapResolver::new);
- let maybe_jsx_resolver = ps.maybe_config_file.as_ref().and_then(|cf| {
- cf.to_maybe_jsx_import_source_module()
- .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone()))
- });
- let maybe_resolver = if maybe_jsx_resolver.is_some() {
- maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver())
- } else {
- maybe_import_map_resolver
- .as_ref()
- .map(|im| im.as_resolver())
- };
- let graph = deno_graph::create_graph(
- vec![(main_module.clone(), deno_graph::ModuleKind::Esm)],
- false,
- maybe_imports,
- &mut cache,
- maybe_resolver,
- maybe_locker,
- None,
- None,
- )
- .await;
- let check_js = ps
- .maybe_config_file
- .as_ref()
- .map(|cf| cf.get_check_js())
- .unwrap_or(false);
- graph_valid(
- &graph,
- ps.flags.type_check_mode != flags::TypeCheckMode::None,
- check_js,
- )?;
-
- // Find all local files in graph
- let mut paths_to_watch: Vec<PathBuf> = graph
- .specifiers()
- .iter()
- .filter_map(|(_, r)| {
- r.as_ref().ok().and_then(|(s, _, _)| s.to_file_path().ok())
- })
- .collect();
-
- // Add the extra files listed in the watch flag
- if let Some(watch_paths) = watch_flag {
- paths_to_watch.extend(watch_paths);
- }
-
- if let Ok(Some(import_map_path)) =
- config_file::resolve_import_map_specifier(
- ps.flags.import_map_path.as_deref(),
- ps.maybe_config_file.as_ref(),
- )
- .map(|ms| ms.and_then(|ref s| s.to_file_path().ok()))
- {
- paths_to_watch.push(import_map_path);
- }
-
- Ok((paths_to_watch, main_module, ps))
- }
- .map(move |result| match result {
- Ok((paths_to_watch, module_info, ps)) => ResolutionResult::Restart {
- paths_to_watch,
- result: Ok((ps, module_info)),
- },
- Err(e) => ResolutionResult::Restart {
- paths_to_watch: vec![PathBuf::from(script2)],
- result: Err(e),
- },
- })
- };
-
/// The FileWatcherModuleExecutor provides module execution with safe dispatching of life-cycle events by tracking the
/// state of any pending events and emitting accordingly on drop in the case of a future
/// cancellation.
@@ -1144,10 +1050,19 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<i32, AnyError> {
}
}
- let operation = |(ps, main_module): (ProcState, ModuleSpecifier)| {
+ let flags = Arc::new(flags);
+ let main_module = resolve_url_or_path(&script)?;
+ let (sender, receiver) = tokio::sync::mpsc::unbounded_channel();
+
+ let operation = |(sender, main_module): (
+ tokio::sync::mpsc::UnboundedSender<Vec<PathBuf>>,
+ ModuleSpecifier,
+ )| {
let flags = flags.clone();
let permissions = Permissions::from_options(&flags.permissions_options());
async move {
+ let ps = ProcState::build_for_file_watcher(flags.clone(), sender.clone())
+ .await?;
// We make use an module executor guard to ensure that unload is always fired when an
// operation is called.
let mut executor = FileWatcherModuleExecutor::new(
@@ -1167,15 +1082,17 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<i32, AnyError> {
}
};
- file_watcher::watch_func(
- resolver,
+ file_watcher::watch_func2(
+ receiver,
operation,
+ (sender, main_module),
file_watcher::PrintConfig {
job_name: "Process".to_string(),
clear_screen: !flags.no_clear_screen,
},
)
.await?;
+
Ok(0)
}
diff --git a/cli/proc_state.rs b/cli/proc_state.rs
index 1606975d0..d90b3f952 100644
--- a/cli/proc_state.rs
+++ b/cli/proc_state.rs
@@ -4,6 +4,7 @@ use crate::cache;
use crate::colors;
use crate::compat;
use crate::compat::NodeEsmResolver;
+use crate::config_file;
use crate::config_file::ConfigFile;
use crate::config_file::MaybeImportsResult;
use crate::deno_dir;
@@ -56,6 +57,7 @@ use log::warn;
use std::collections::HashSet;
use std::env;
use std::ops::Deref;
+use std::path::PathBuf;
use std::sync::Arc;
/// This structure represents state of single "deno" program.
@@ -81,6 +83,7 @@ pub struct Inner {
pub shared_array_buffer_store: SharedArrayBufferStore,
pub compiled_wasm_module_store: CompiledWasmModuleStore,
maybe_resolver: Option<Arc<dyn deno_graph::source::Resolver + Send + Sync>>,
+ maybe_file_watcher_reporter: Option<FileWatcherReporter>,
}
impl Deref for ProcState {
@@ -92,6 +95,41 @@ impl Deref for ProcState {
impl ProcState {
pub async fn build(flags: Arc<flags::Flags>) -> Result<Self, AnyError> {
+ Self::build_with_sender(flags, None).await
+ }
+
+ pub async fn build_for_file_watcher(
+ flags: Arc<flags::Flags>,
+ files_to_watch_sender: tokio::sync::mpsc::UnboundedSender<Vec<PathBuf>>,
+ ) -> Result<Self, AnyError> {
+ let ps = Self::build_with_sender(
+ flags.clone(),
+ Some(files_to_watch_sender.clone()),
+ )
+ .await?;
+
+ // Add the extra files listed in the watch flag
+ if let Some(watch_paths) = &flags.watch {
+ files_to_watch_sender.send(watch_paths.clone()).unwrap();
+ }
+
+ if let Ok(Some(import_map_path)) =
+ config_file::resolve_import_map_specifier(
+ ps.flags.import_map_path.as_deref(),
+ ps.maybe_config_file.as_ref(),
+ )
+ .map(|ms| ms.and_then(|ref s| s.to_file_path().ok()))
+ {
+ files_to_watch_sender.send(vec![import_map_path]).unwrap();
+ }
+
+ Ok(ps)
+ }
+
+ async fn build_with_sender(
+ flags: Arc<flags::Flags>,
+ maybe_sender: Option<tokio::sync::mpsc::UnboundedSender<Vec<PathBuf>>>,
+ ) -> Result<Self, AnyError> {
let maybe_custom_root = flags
.cache_path
.clone()
@@ -209,6 +247,12 @@ impl ProcState {
None
};
+ let maybe_file_watcher_reporter =
+ maybe_sender.map(|sender| FileWatcherReporter {
+ sender,
+ file_paths: Arc::new(Mutex::new(vec![])),
+ });
+
Ok(ProcState(Arc::new(Inner {
dir,
coverage_dir,
@@ -225,6 +269,7 @@ impl ProcState {
shared_array_buffer_store,
compiled_wasm_module_store,
maybe_resolver,
+ maybe_file_watcher_reporter,
})))
}
@@ -358,6 +403,13 @@ impl ProcState {
reload: reload_on_watch,
};
+ let maybe_file_watcher_reporter: Option<&dyn deno_graph::source::Reporter> =
+ if let Some(reporter) = &self.maybe_file_watcher_reporter {
+ Some(reporter)
+ } else {
+ None
+ };
+
let graph = create_graph(
roots.clone(),
is_dynamic,
@@ -366,7 +418,7 @@ impl ProcState {
maybe_resolver,
maybe_locker,
None,
- None,
+ maybe_file_watcher_reporter,
)
.await;
@@ -719,3 +771,27 @@ fn source_map_from_code(code: String) -> Option<Vec<u8>> {
None
}
}
+
+#[derive(Debug)]
+struct FileWatcherReporter {
+ sender: tokio::sync::mpsc::UnboundedSender<Vec<PathBuf>>,
+ file_paths: Arc<Mutex<Vec<PathBuf>>>,
+}
+
+impl deno_graph::source::Reporter for FileWatcherReporter {
+ fn on_load(
+ &self,
+ specifier: &ModuleSpecifier,
+ modules_done: usize,
+ modules_total: usize,
+ ) {
+ let mut file_paths = self.file_paths.lock();
+ if specifier.scheme() == "file" {
+ file_paths.push(specifier.to_file_path().unwrap());
+ }
+
+ if modules_done == modules_total {
+ self.sender.send(file_paths.drain(..).collect()).unwrap();
+ }
+ }
+}
diff --git a/cli/tests/integration/watcher_tests.rs b/cli/tests/integration/watcher_tests.rs
index a74b7a6d3..ec2588911 100644
--- a/cli/tests/integration/watcher_tests.rs
+++ b/cli/tests/integration/watcher_tests.rs
@@ -48,15 +48,22 @@ fn read_all_lints(stderr_lines: &mut impl Iterator<Item = String>) -> String {
str
}
-fn wait_for(s: &str, lines: &mut impl Iterator<Item = String>) {
+fn wait_for(
+ condition: impl Fn(&str) -> bool,
+ lines: &mut impl Iterator<Item = String>,
+) {
loop {
let msg = lines.next().unwrap();
- if msg.contains(s) {
+ if condition(&msg) {
break;
}
}
}
+fn wait_contains(s: &str, lines: &mut impl Iterator<Item = String>) {
+ wait_for(|msg| msg.contains(s), lines)
+}
+
fn read_line(s: &str, lines: &mut impl Iterator<Item = String>) -> String {
lines.find(|m| m.contains(s)).unwrap()
}
@@ -408,7 +415,7 @@ fn bundle_js_watch() {
assert_contains!(stderr_lines.next().unwrap(), "mod6.bundle.js");
let file = PathBuf::from(&bundle);
assert!(file.is_file());
- wait_for("Bundle finished", &mut stderr_lines);
+ wait_contains("Bundle finished", &mut stderr_lines);
write(&file_to_watch, "console.log('Hello world2');").unwrap();
@@ -420,14 +427,14 @@ fn bundle_js_watch() {
assert_contains!(stderr_lines.next().unwrap(), "mod6.bundle.js");
let file = PathBuf::from(&bundle);
assert!(file.is_file());
- wait_for("Bundle finished", &mut stderr_lines);
+ wait_contains("Bundle finished", &mut stderr_lines);
// Confirm that the watcher keeps on working even if the file is updated and has invalid syntax
write(&file_to_watch, "syntax error ^^").unwrap();
assert_contains!(stderr_lines.next().unwrap(), "File change detected!");
assert_contains!(stderr_lines.next().unwrap(), "error: ");
- wait_for("Bundle failed", &mut stderr_lines);
+ wait_contains("Bundle failed", &mut stderr_lines);
check_alive_then_kill(deno);
}
@@ -470,15 +477,15 @@ fn bundle_watch_not_exit() {
assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.ts");
assert_contains!(stderr_lines.next().unwrap(), "target.js");
- wait_for("Bundle finished", &mut stderr_lines);
+ wait_contains("Bundle finished", &mut stderr_lines);
// bundled file is created
assert!(target_file.is_file());
check_alive_then_kill(deno);
}
-#[flaky_test::flaky_test]
-fn run_watch() {
+#[test]
+fn run_watch_no_dynamic() {
let t = TempDir::new();
let file_to_watch = t.path().join("file_to_watch.js");
write(&file_to_watch, "console.log('Hello world');").unwrap();
@@ -488,6 +495,8 @@ fn run_watch() {
.arg("run")
.arg("--watch")
.arg("--unstable")
+ .arg("-L")
+ .arg("debug")
.arg(&file_to_watch)
.env("NO_COLOR", "1")
.stdout(std::process::Stdio::piped())
@@ -496,15 +505,21 @@ fn run_watch() {
.unwrap();
let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
- assert_contains!(stdout_lines.next().unwrap(), "Hello world");
- wait_for("Process finished", &mut stderr_lines);
+ wait_contains("Hello world", &mut stdout_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
+ &mut stderr_lines,
+ );
// Change content of the file
write(&file_to_watch, "console.log('Hello world2');").unwrap();
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- assert_contains!(stdout_lines.next().unwrap(), "Hello world2");
- wait_for("Process finished", &mut stderr_lines);
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("Hello world2", &mut stdout_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
+ &mut stderr_lines,
+ );
// Add dependency
let another_file = t.path().join("another_file.js");
@@ -515,23 +530,32 @@ fn run_watch() {
)
.unwrap();
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- assert_contains!(stdout_lines.next().unwrap(), '0');
- wait_for("Process finished", &mut stderr_lines);
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("0", &mut stdout_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("another_file.js"),
+ &mut stderr_lines,
+ );
// Confirm that restarting occurs when a new file is updated
write(&another_file, "export const foo = 42;").unwrap();
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- assert_contains!(stdout_lines.next().unwrap(), "42");
- wait_for("Process finished", &mut stderr_lines);
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("42", &mut stdout_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
+ &mut stderr_lines,
+ );
// Confirm that the watcher keeps on working even if the file is updated and has invalid syntax
write(&file_to_watch, "syntax error ^^").unwrap();
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- assert_contains!(stderr_lines.next().unwrap(), "error:");
- wait_for("Process failed", &mut stderr_lines);
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("error:", &mut stderr_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
+ &mut stderr_lines,
+ );
// Then restore the file
write(
@@ -540,23 +564,29 @@ fn run_watch() {
)
.unwrap();
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- assert_contains!(stdout_lines.next().unwrap(), "42");
- wait_for("Process finished", &mut stderr_lines);
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("42", &mut stdout_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("another_file.js"),
+ &mut stderr_lines,
+ );
// Update the content of the imported file with invalid syntax
write(&another_file, "syntax error ^^").unwrap();
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- assert_contains!(stderr_lines.next().unwrap(), "error:");
- wait_for("Process failed", &mut stderr_lines);
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("error:", &mut stderr_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("another_file.js"),
+ &mut stderr_lines,
+ );
// Modify the imported file and make sure that restarting occurs
write(&another_file, "export const foo = 'modified!';").unwrap();
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- assert_contains!(stdout_lines.next().unwrap(), "modified!");
- wait_for("Process finished", &mut stderr_lines);
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("modified!", &mut stdout_lines);
+ wait_contains("Watching paths", &mut stderr_lines);
check_alive_then_kill(child);
}
@@ -581,6 +611,8 @@ fn run_watch_external_watch_files() {
.current_dir(util::testdata_path())
.arg("run")
.arg(watch_arg)
+ .arg("-L")
+ .arg("debug")
.arg("--unstable")
.arg(&file_to_watch)
.env("NO_COLOR", "1")
@@ -589,15 +621,20 @@ fn run_watch_external_watch_files() {
.spawn()
.unwrap();
let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
-
- assert_contains!(stdout_lines.next().unwrap(), "Hello world");
- wait_for("Process finished", &mut stderr_lines);
+ wait_contains("Process started", &mut stderr_lines);
+ wait_contains("Hello world", &mut stdout_lines);
+ wait_for(
+ |m| {
+ m.contains("Watching paths") && m.contains("external_file_to_watch.txt")
+ },
+ &mut stderr_lines,
+ );
// Change content of the external file
write(&external_file_to_watch, "Hello world2").unwrap();
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- wait_for("Process finished", &mut stderr_lines);
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("Process finished", &mut stderr_lines);
check_alive_then_kill(child);
}
@@ -625,6 +662,8 @@ fn run_watch_load_unload_events() {
.arg("run")
.arg("--watch")
.arg("--unstable")
+ .arg("-L")
+ .arg("debug")
.arg(&file_to_watch)
.env("NO_COLOR", "1")
.env("DENO_FUTURE_CHECK", "1")
@@ -635,7 +674,11 @@ fn run_watch_load_unload_events() {
let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
// Wait for the first load event to fire
- assert_contains!(stdout_lines.next().unwrap(), "load");
+ wait_contains("load", &mut stdout_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
+ &mut stderr_lines,
+ );
// Change content of the file, this time without an interval to keep it alive.
write(
@@ -653,18 +696,16 @@ fn run_watch_load_unload_events() {
.unwrap();
// Wait for the restart
- let next_line = stderr_lines.next().unwrap();
- assert_contains!(&next_line, "Process started");
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
+ wait_contains("Restarting", &mut stderr_lines);
// Confirm that the unload event was dispatched from the first run
- assert_contains!(stdout_lines.next().unwrap(), "unload");
+ wait_contains("unload", &mut stdout_lines);
// Followed by the load event of the second run
- assert_contains!(stdout_lines.next().unwrap(), "load");
+ wait_contains("load", &mut stdout_lines);
// Which is then unloaded as there is nothing keeping it alive.
- assert_contains!(stdout_lines.next().unwrap(), "unload");
+ wait_contains("unload", &mut stdout_lines);
check_alive_then_kill(child);
}
@@ -680,6 +721,8 @@ fn run_watch_not_exit() {
.arg("run")
.arg("--watch")
.arg("--unstable")
+ .arg("-L")
+ .arg("debug")
.arg(&file_to_watch)
.env("NO_COLOR", "1")
.env("DENO_FUTURE_CHECK", "1")
@@ -689,19 +732,19 @@ fn run_watch_not_exit() {
.unwrap();
let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
- let next_line = stderr_lines.next().unwrap();
- assert_contains!(&next_line, "Process started");
- assert_contains!(stderr_lines.next().unwrap(), "error:");
- assert_contains!(stderr_lines.next().unwrap(), "Process failed");
+ wait_contains("Process started", &mut stderr_lines);
+ wait_contains("error:", &mut stderr_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
+ &mut stderr_lines,
+ );
// Make sure the watcher actually restarts and works fine with the proper syntax
write(&file_to_watch, "console.log(42);").unwrap();
- let next_line = stderr_lines.next().unwrap();
- assert_contains!(&next_line, CLEAR_SCREEN);
- assert_contains!(&next_line, "Restarting");
- assert_contains!(stdout_lines.next().unwrap(), "42");
- wait_for("Process finished", &mut stderr_lines);
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("42", &mut stdout_lines);
+ wait_contains("Process finished", &mut stderr_lines);
check_alive_then_kill(child);
}
@@ -756,7 +799,7 @@ fn run_watch_with_import_map_and_relative_paths() {
check_alive_then_kill(child);
}
-#[flaky_test]
+#[test]
fn test_watch() {
let t = TempDir::new();
@@ -779,7 +822,7 @@ fn test_watch() {
stdout_lines.next().unwrap(),
"0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out"
);
- wait_for("Test finished", &mut stderr_lines);
+ wait_contains("Test finished", &mut stderr_lines);
let foo_file = t.path().join("foo.js");
let bar_file = t.path().join("bar.js");
@@ -806,7 +849,7 @@ fn test_watch() {
stdout_lines.next();
stdout_lines.next();
stdout_lines.next();
- wait_for("Test finished", &mut stderr_lines);
+ wait_contains("Test finished", &mut stderr_lines);
// Change content of the file
write(
@@ -821,7 +864,7 @@ fn test_watch() {
stdout_lines.next();
stdout_lines.next();
stdout_lines.next();
- wait_for("Test finished", &mut stderr_lines);
+ wait_contains("Test finished", &mut stderr_lines);
// Add test
let another_test = t.path().join("new_test.js");
@@ -832,7 +875,7 @@ fn test_watch() {
stdout_lines.next();
stdout_lines.next();
stdout_lines.next();
- wait_for("Test finished", &mut stderr_lines);
+ wait_contains("Test finished", &mut stderr_lines);
// Confirm that restarting occurs when a new file is updated
write(&another_test, "Deno.test('another one', () => 3 + 3); Deno.test('another another one', () => 4 + 4)")
@@ -844,7 +887,7 @@ fn test_watch() {
stdout_lines.next();
stdout_lines.next();
stdout_lines.next();
- wait_for("Test finished", &mut stderr_lines);
+ wait_contains("Test finished", &mut stderr_lines);
// Confirm that the watcher keeps on working even if the file is updated and has invalid syntax
write(&another_test, "syntax error ^^").unwrap();
@@ -860,7 +903,7 @@ fn test_watch() {
stdout_lines.next();
stdout_lines.next();
stdout_lines.next();
- wait_for("Test finished", &mut stderr_lines);
+ wait_contains("Test finished", &mut stderr_lines);
// Confirm that the watcher keeps on working even if the file is updated and the test fails
// This also confirms that it restarts when dependencies change
@@ -872,9 +915,9 @@ fn test_watch() {
assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
assert_contains!(stdout_lines.next().unwrap(), "FAILED");
- wait_for("test result", &mut stdout_lines);
+ wait_contains("test result", &mut stdout_lines);
stdout_lines.next();
- wait_for("Test finished", &mut stderr_lines);
+ wait_contains("Test finished", &mut stderr_lines);
// Then restore the file
write(&foo_file, "export default function foo() { 1 + 1 }").unwrap();
@@ -884,7 +927,7 @@ fn test_watch() {
stdout_lines.next();
stdout_lines.next();
stdout_lines.next();
- wait_for("Test finished", &mut stderr_lines);
+ wait_contains("Test finished", &mut stderr_lines);
// Test that circular dependencies work fine
write(
@@ -923,7 +966,7 @@ fn test_watch_doc() {
stdout_lines.next().unwrap(),
"0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out"
);
- wait_for("Test finished", &mut stderr_lines);
+ wait_contains("Test finished", &mut stderr_lines);
let foo_file = t.path().join("foo.ts");
write(
@@ -979,23 +1022,48 @@ fn test_watch_module_graph_error_referrer() {
let line3 = stderr_lines.next().unwrap();
assert_contains!(&line3, " at ");
assert_contains!(&line3, "file_to_watch.js");
- wait_for("Process failed", &mut stderr_lines);
+ wait_contains("Process finished", &mut stderr_lines);
check_alive_then_kill(child);
}
#[test]
-fn watch_with_no_clear_screen_flag() {
+fn run_watch_dynamic_imports() {
let t = TempDir::new();
let file_to_watch = t.path().join("file_to_watch.js");
- write(&file_to_watch, "export const foo = 0;").unwrap();
+ write(
+ &file_to_watch,
+ r#"
+ console.log("Hopefully dynamic import will be watched...");
+ await import("./imported.js");
+ "#,
+ )
+ .unwrap();
+ let file_to_watch2 = t.path().join("imported.js");
+ write(
+ &file_to_watch2,
+ r#"
+ import "./imported2.js";
+ console.log("I'm dynamically imported and I cause restarts!");
+ "#,
+ )
+ .unwrap();
+ let file_to_watch3 = t.path().join("imported2.js");
+ write(
+ &file_to_watch3,
+ r#"
+ console.log("I'm statically imported from the dynamic import");
+ "#,
+ )
+ .unwrap();
- // choose deno run subcommand to test --no-clear-screen flag
let mut child = util::deno_cmd()
.current_dir(util::testdata_path())
.arg("run")
.arg("--watch")
- .arg("--no-clear-screen")
.arg("--unstable")
+ .arg("--allow-read")
+ .arg("-L")
+ .arg("debug")
.arg(&file_to_watch)
.env("NO_COLOR", "1")
.env("DENO_FUTURE_CHECK", "1")
@@ -1003,30 +1071,49 @@ fn watch_with_no_clear_screen_flag() {
.stderr(std::process::Stdio::piped())
.spawn()
.unwrap();
- let (_, mut stderr_lines) = child_lines(&mut child);
+ let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
- let next_line = stderr_lines.next().unwrap();
+ assert_contains!(stderr_lines.next().unwrap(), "Process started");
- // no clear screen
- assert!(!&next_line.contains(CLEAR_SCREEN));
- assert_contains!(&next_line, "Process started");
- assert_contains!(
- stderr_lines.next().unwrap(),
- "Process finished. Restarting on file change..."
+ wait_contains(
+ "Hopefully dynamic import will be watched...",
+ &mut stdout_lines,
+ );
+ wait_contains(
+ "I'm statically imported from the dynamic import",
+ &mut stdout_lines,
+ );
+ wait_contains(
+ "I'm dynamically imported and I cause restarts!",
+ &mut stdout_lines,
);
- // Change content of the file
- write(&file_to_watch, "export const bar = 0;").unwrap();
-
- let next_line = stderr_lines.next().unwrap();
+ wait_contains("finished", &mut stderr_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("imported2.js"),
+ &mut stderr_lines,
+ );
- // no clear screen
- assert!(!&next_line.contains(CLEAR_SCREEN));
+ write(
+ &file_to_watch3,
+ r#"
+ console.log("I'm statically imported from the dynamic import and I've changed");
+ "#,
+ )
+ .unwrap();
- assert_contains!(&next_line, "Watcher File change detected! Restarting!");
- assert_contains!(
- stderr_lines.next().unwrap(),
- "Process finished. Restarting on file change..."
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains(
+ "Hopefully dynamic import will be watched...",
+ &mut stdout_lines,
+ );
+ wait_contains(
+ "I'm statically imported from the dynamic import and I've changed",
+ &mut stdout_lines,
+ );
+ wait_contains(
+ "I'm dynamically imported and I cause restarts!",
+ &mut stdout_lines,
);
check_alive_then_kill(child);