mirror of
https://github.com/denoland/deno.git
synced 2025-08-04 02:48:24 +00:00
parent
48c6f71787
commit
84c793275b
17 changed files with 917 additions and 1244 deletions
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::args::Flags;
|
||||
use crate::colors;
|
||||
use crate::util::fs::canonicalize_path;
|
||||
|
||||
|
@ -21,6 +22,7 @@ use std::time::Duration;
|
|||
use tokio::select;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::mpsc::UnboundedReceiver;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
use tokio::time::sleep;
|
||||
|
||||
const CLEAR_SCREEN: &str = "\x1B[2J\x1B[1;1H";
|
||||
|
@ -66,7 +68,7 @@ impl DebouncedReceiver {
|
|||
}
|
||||
}
|
||||
|
||||
async fn error_handler<F>(watch_future: F)
|
||||
async fn error_handler<F>(watch_future: F) -> bool
|
||||
where
|
||||
F: Future<Output = Result<(), AnyError>>,
|
||||
{
|
||||
|
@ -81,42 +83,9 @@ where
|
|||
colors::red_bold("error"),
|
||||
error_string.trim_start_matches("error: ")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ResolutionResult<T> {
|
||||
Restart {
|
||||
paths_to_watch: Vec<PathBuf>,
|
||||
result: Result<T, AnyError>,
|
||||
},
|
||||
Ignore,
|
||||
}
|
||||
|
||||
async fn next_restart<R, T, F>(
|
||||
resolver: &mut R,
|
||||
debounced_receiver: &mut DebouncedReceiver,
|
||||
) -> (Vec<PathBuf>, Result<T, AnyError>)
|
||||
where
|
||||
R: FnMut(Option<Vec<PathBuf>>) -> F,
|
||||
F: Future<Output = ResolutionResult<T>>,
|
||||
{
|
||||
loop {
|
||||
let changed = debounced_receiver.recv().await;
|
||||
match resolver(changed).await {
|
||||
ResolutionResult::Ignore => {
|
||||
log::debug!("File change ignored")
|
||||
}
|
||||
ResolutionResult::Restart {
|
||||
mut paths_to_watch,
|
||||
result,
|
||||
} => {
|
||||
// watch the current directory when empty
|
||||
if paths_to_watch.is_empty() {
|
||||
paths_to_watch.push(PathBuf::from("."));
|
||||
}
|
||||
return (paths_to_watch, result);
|
||||
}
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,138 +108,26 @@ fn create_print_after_restart_fn(clear_screen: bool) -> impl Fn() {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// of the watcher, and can also return a value to be passed to `operation`.
|
||||
/// It returns a [`ResolutionResult`], which can either instruct the watcher to restart or ignore the change.
|
||||
/// This always contains paths to watch;
|
||||
///
|
||||
/// - `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_func<R, O, T, F1, F2>(
|
||||
mut resolver: R,
|
||||
mut operation: O,
|
||||
print_config: PrintConfig,
|
||||
) -> Result<(), AnyError>
|
||||
where
|
||||
R: FnMut(Option<Vec<PathBuf>>) -> F1,
|
||||
O: FnMut(T) -> F2,
|
||||
F1: Future<Output = ResolutionResult<T>>,
|
||||
F2: Future<Output = Result<(), AnyError>>,
|
||||
{
|
||||
let (sender, mut receiver) = DebouncedReceiver::new_with_sender();
|
||||
|
||||
let PrintConfig {
|
||||
job_name,
|
||||
clear_screen,
|
||||
} = print_config;
|
||||
|
||||
// Store previous data. If module resolution fails at some point, the watcher will try to
|
||||
// continue watching files using these data.
|
||||
let mut paths_to_watch;
|
||||
let mut resolution_result;
|
||||
|
||||
let print_after_restart = create_print_after_restart_fn(clear_screen);
|
||||
|
||||
match resolver(None).await {
|
||||
ResolutionResult::Ignore => {
|
||||
// The only situation where it makes sense to ignore the initial 'change'
|
||||
// is if the command isn't supposed to do anything until something changes,
|
||||
// e.g. a variant of `deno test` which doesn't run the entire test suite to start with,
|
||||
// but instead does nothing until you make a change.
|
||||
//
|
||||
// In that case, this is probably the correct output.
|
||||
info!(
|
||||
"{} Waiting for file changes...",
|
||||
colors::intense_blue("Watcher"),
|
||||
);
|
||||
|
||||
let (paths, result) = next_restart(&mut resolver, &mut receiver).await;
|
||||
paths_to_watch = paths;
|
||||
resolution_result = result;
|
||||
|
||||
print_after_restart();
|
||||
}
|
||||
ResolutionResult::Restart {
|
||||
paths_to_watch: mut paths,
|
||||
result,
|
||||
} => {
|
||||
// watch the current directory when empty
|
||||
if paths.is_empty() {
|
||||
paths.push(PathBuf::from("."));
|
||||
}
|
||||
paths_to_watch = paths;
|
||||
resolution_result = result;
|
||||
}
|
||||
};
|
||||
|
||||
info!("{} {} started.", colors::intense_blue("Watcher"), job_name,);
|
||||
|
||||
loop {
|
||||
let mut watcher = new_watcher(sender.clone())?;
|
||||
add_paths_to_watcher(&mut watcher, &paths_to_watch);
|
||||
|
||||
match resolution_result {
|
||||
Ok(operation_arg) => {
|
||||
let fut = error_handler(operation(operation_arg));
|
||||
select! {
|
||||
(paths, result) = next_restart(&mut resolver, &mut receiver) => {
|
||||
if result.is_ok() {
|
||||
paths_to_watch = paths;
|
||||
}
|
||||
resolution_result = result;
|
||||
|
||||
print_after_restart();
|
||||
continue;
|
||||
},
|
||||
_ = fut => {},
|
||||
};
|
||||
|
||||
info!(
|
||||
"{} {} finished. Restarting on file change...",
|
||||
colors::intense_blue("Watcher"),
|
||||
job_name,
|
||||
);
|
||||
}
|
||||
Err(error) => {
|
||||
eprintln!("{}: {}", colors::red_bold("error"), error);
|
||||
info!(
|
||||
"{} {} failed. Restarting on file change...",
|
||||
colors::intense_blue("Watcher"),
|
||||
job_name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let (paths, result) = next_restart(&mut resolver, &mut receiver).await;
|
||||
if result.is_ok() {
|
||||
paths_to_watch = paths;
|
||||
}
|
||||
resolution_result = result;
|
||||
|
||||
print_after_restart();
|
||||
|
||||
drop(watcher);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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: UnboundedReceiver<Vec<PathBuf>>,
|
||||
mut operation: O,
|
||||
operation_args: T,
|
||||
pub async fn watch_func<O, F>(
|
||||
mut flags: Flags,
|
||||
print_config: PrintConfig,
|
||||
mut operation: O,
|
||||
) -> Result<(), AnyError>
|
||||
where
|
||||
O: FnMut(T) -> Result<F, AnyError>,
|
||||
O: FnMut(
|
||||
Flags,
|
||||
UnboundedSender<Vec<PathBuf>>,
|
||||
Option<Vec<PathBuf>>,
|
||||
) -> Result<F, AnyError>,
|
||||
F: Future<Output = Result<(), AnyError>>,
|
||||
{
|
||||
let (paths_to_watch_sender, mut paths_to_watch_receiver) =
|
||||
tokio::sync::mpsc::unbounded_channel();
|
||||
let (watcher_sender, mut watcher_receiver) =
|
||||
DebouncedReceiver::new_with_sender();
|
||||
|
||||
|
@ -303,6 +160,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
let mut changed_paths = None;
|
||||
loop {
|
||||
// We may need to give the runtime a tick to settle, as cancellations may need to propagate
|
||||
// to tasks. We choose yielding 10 times to the runtime as a decent heuristic. If watch tests
|
||||
|
@ -320,21 +178,34 @@ where
|
|||
add_paths_to_watcher(&mut watcher, &maybe_paths.unwrap());
|
||||
}
|
||||
};
|
||||
let operation_future = error_handler(operation(operation_args.clone())?);
|
||||
let operation_future = error_handler(operation(
|
||||
flags.clone(),
|
||||
paths_to_watch_sender.clone(),
|
||||
changed_paths.take(),
|
||||
)?);
|
||||
|
||||
// don't reload dependencies after the first run
|
||||
flags.reload = false;
|
||||
|
||||
select! {
|
||||
_ = receiver_future => {},
|
||||
_ = watcher_receiver.recv() => {
|
||||
received_changed_paths = watcher_receiver.recv() => {
|
||||
print_after_restart();
|
||||
changed_paths = received_changed_paths;
|
||||
continue;
|
||||
},
|
||||
_ = operation_future => {
|
||||
success = operation_future => {
|
||||
consume_paths_to_watch(&mut watcher, &mut paths_to_watch_receiver);
|
||||
// TODO(bartlomieju): print exit code here?
|
||||
info!(
|
||||
"{} {} finished. Restarting on file change...",
|
||||
"{} {} {}. Restarting on file change...",
|
||||
colors::intense_blue("Watcher"),
|
||||
job_name,
|
||||
if success {
|
||||
"finished"
|
||||
} else {
|
||||
"failed"
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
@ -347,8 +218,9 @@ where
|
|||
};
|
||||
select! {
|
||||
_ = receiver_future => {},
|
||||
_ = watcher_receiver.recv() => {
|
||||
received_changed_paths = watcher_receiver.recv() => {
|
||||
print_after_restart();
|
||||
changed_paths = received_changed_paths;
|
||||
continue;
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue