0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-03 09:31:22 -05:00

feat(watch): support watching external files (#13087)

This commit is contained in:
Jesper van den Ende 2021-12-15 22:04:43 +01:00 committed by GitHub
parent d8e7e3fbe3
commit 5a3ded6611
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 123 additions and 28 deletions

View file

@ -251,7 +251,7 @@ pub struct Flags {
pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
pub v8_flags: Vec<String>,
pub version: bool,
pub watch: bool,
pub watch: Option<Vec<PathBuf>>,
}
fn join_paths(allowlist: &[PathBuf], d: &str) -> String {
@ -549,7 +549,7 @@ fn bundle_subcommand<'a, 'b>() -> App<'a, 'b> {
.required(true),
)
.arg(Arg::with_name("out_file").takes_value(true).required(false))
.arg(watch_arg())
.arg(watch_arg(false))
.about("Bundle module and dependencies into single file")
.long_about(
"Output a single JavaScript file with all dependencies.
@ -882,7 +882,7 @@ Ignore formatting a file by adding an ignore comment at the top of the file:
.multiple(true)
.required(false),
)
.arg(watch_arg())
.arg(watch_arg(false))
.arg(
Arg::with_name("options-use-tabs")
.long("options-use-tabs")
@ -1158,7 +1158,7 @@ Ignore linting a file by adding an ignore comment at the top of the file:
.multiple(true)
.required(false),
)
.arg(watch_arg())
.arg(watch_arg(false))
}
fn repl_subcommand<'a, 'b>() -> App<'a, 'b> {
@ -1177,7 +1177,7 @@ fn repl_subcommand<'a, 'b>() -> App<'a, 'b> {
fn run_subcommand<'a, 'b>() -> App<'a, 'b> {
runtime_args(SubCommand::with_name("run"), true, true)
.arg(
watch_arg()
watch_arg(true)
.conflicts_with("inspect")
.conflicts_with("inspect-brk"),
)
@ -1305,7 +1305,7 @@ fn test_subcommand<'a, 'b>() -> App<'a, 'b> {
.multiple(true),
)
.arg(
watch_arg()
watch_arg(false)
.conflicts_with("no-run")
.conflicts_with("coverage"),
)
@ -1640,14 +1640,29 @@ fn compat_arg<'a, 'b>() -> Arg<'a, 'b> {
.help("Node compatibility mode. Currently only enables built-in node modules like 'fs' and globals like 'process'.")
}
fn watch_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name("watch")
fn watch_arg<'a, 'b>(takes_files: bool) -> Arg<'a, 'b> {
let arg = Arg::with_name("watch")
.long("watch")
.help("UNSTABLE: Watch for file changes and restart process automatically")
.long_help(
.help("UNSTABLE: Watch for file changes and restart process automatically");
if takes_files {
arg
.value_name("FILES")
.min_values(0)
.takes_value(true)
.use_delimiter(true)
.require_equals(true)
.long_help(
"UNSTABLE: Watch for file changes and restart process automatically.
Local files from entry point module graph are watched by default.
Additional paths might be watched by passing them as arguments to this flag.",
)
} else {
arg.long_help(
"UNSTABLE: Watch for file changes and restart process automatically.
Only local files from entry point module graph are watched.",
)
}
}
fn no_check_arg<'a, 'b>() -> Arg<'a, 'b> {
@ -1743,7 +1758,7 @@ fn bundle_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
None
};
flags.watch = matches.is_present("watch");
watch_arg_parse(flags, matches, false);
flags.subcommand = DenoSubcommand::Bundle(BundleFlags {
source_file,
@ -1874,7 +1889,7 @@ fn eval_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
fn fmt_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
config_arg_parse(flags, matches);
flags.watch = matches.is_present("watch");
watch_arg_parse(flags, matches, false);
let files = match matches.values_of("files") {
Some(f) => f.map(PathBuf::from).collect(),
None => vec![],
@ -1996,7 +2011,7 @@ fn lsp_parse(flags: &mut Flags, _matches: &clap::ArgMatches) {
fn lint_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
config_arg_parse(flags, matches);
flags.watch = matches.is_present("watch");
watch_arg_parse(flags, matches, false);
let files = match matches.values_of("files") {
Some(f) => f.map(PathBuf::from).collect(),
None => vec![],
@ -2061,7 +2076,7 @@ fn run_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
flags.argv.push(v);
}
flags.watch = matches.is_present("watch");
watch_arg_parse(flags, matches, true);
flags.subcommand = DenoSubcommand::Run(RunFlags { script });
}
@ -2135,7 +2150,7 @@ fn test_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
};
flags.coverage_dir = matches.value_of("coverage").map(String::from);
flags.watch = matches.is_present("watch");
watch_arg_parse(flags, matches, false);
flags.subcommand = DenoSubcommand::Test(TestFlags {
no_run,
doc,
@ -2409,6 +2424,20 @@ fn inspect_arg_validate(val: String) -> Result<(), String> {
}
}
fn watch_arg_parse(
flags: &mut Flags,
matches: &clap::ArgMatches,
allow_extra: bool,
) {
if allow_extra {
if let Some(f) = matches.values_of("watch") {
flags.watch = Some(f.map(PathBuf::from).collect());
}
} else if matches.is_present("watch") {
flags.watch = Some(vec![]);
}
}
// TODO(ry) move this to utility module and add test.
/// Strips fragment part of URL. Panics on bad URL.
pub fn resolve_urls(urls: Vec<String>) -> Vec<String> {
@ -2513,7 +2542,24 @@ mod tests {
subcommand: DenoSubcommand::Run(RunFlags {
script: "script.ts".to_string(),
}),
watch: true,
watch: Some(vec![]),
..Flags::default()
}
);
}
#[test]
fn run_watch_with_external() {
let r =
flags_from_vec(svec!["deno", "run", "--watch=file1,file2", "script.ts"]);
let flags = r.unwrap();
assert_eq!(
flags,
Flags {
subcommand: DenoSubcommand::Run(RunFlags {
script: "script.ts".to_string(),
}),
watch: Some(vec![PathBuf::from("file1"), PathBuf::from("file2")]),
..Flags::default()
}
);
@ -2746,7 +2792,7 @@ mod tests {
single_quote: None,
prose_wrap: None,
}),
watch: true,
watch: Some(vec![]),
..Flags::default()
}
);
@ -2773,7 +2819,7 @@ mod tests {
single_quote: None,
prose_wrap: None,
}),
watch: true,
watch: Some(vec![]),
..Flags::default()
}
);
@ -2821,7 +2867,7 @@ mod tests {
prose_wrap: None,
}),
config_path: Some("deno.jsonc".to_string()),
watch: true,
watch: Some(vec![]),
..Flags::default()
}
);
@ -3543,7 +3589,7 @@ mod tests {
source_file: "source.ts".to_string(),
out_file: None,
}),
watch: true,
watch: Some(vec![]),
..Flags::default()
}
)
@ -4280,7 +4326,7 @@ mod tests {
ignore: vec![],
concurrent_jobs: NonZeroUsize::new(1).unwrap(),
}),
watch: false,
watch: None,
..Flags::default()
}
);
@ -4303,7 +4349,7 @@ mod tests {
ignore: vec![],
concurrent_jobs: NonZeroUsize::new(1).unwrap(),
}),
watch: true,
watch: Some(vec![]),
..Flags::default()
}
);

View file

@ -577,7 +577,8 @@ async fn lint_command(
None
};
tools::lint::lint(maybe_lint_config, lint_flags, flags.watch).await?;
tools::lint::lint(maybe_lint_config, lint_flags, flags.watch.is_some())
.await?;
Ok(0)
}
@ -894,7 +895,7 @@ async fn bundle_command(
}
};
if flags.watch {
if flags.watch.is_some() {
file_watcher::watch_func(resolver, operation, "Bundle").await?;
} else {
let module_graph =
@ -943,7 +944,8 @@ async fn format_command(
return Ok(0);
}
tools::fmt::format(fmt_flags, flags.watch, maybe_fmt_config).await?;
tools::fmt::format(fmt_flags, flags.watch.is_some(), maybe_fmt_config)
.await?;
Ok(0)
}
@ -1011,6 +1013,7 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<i32, AnyError> {
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?;
@ -1067,6 +1070,11 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<i32, AnyError> {
})
.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 Some(import_map) = ps.flags.import_map_path.as_ref() {
paths_to_watch
.push(fs_util::resolve_from_cwd(std::path::Path::new(import_map))?);
@ -1180,7 +1188,7 @@ async fn run_command(
return run_from_stdin(flags).await;
}
if flags.watch {
if flags.watch.is_some() {
return run_with_watch(flags, run_flags.script).await;
}
@ -1294,7 +1302,7 @@ async fn test_command(
);
}
if flags.watch {
if flags.watch.is_some() {
tools::test::run_tests_with_watch(
flags,
test_flags.include,

View file

@ -575,6 +575,47 @@ fn run_watch() {
check_alive_then_kill(child);
}
#[test]
fn run_watch_external_watch_files() {
let t = TempDir::new().unwrap();
let file_to_watch = t.path().join("file_to_watch.js");
write(&file_to_watch, "console.log('Hello world');").unwrap();
let external_file_to_watch = t.path().join("external_file_to_watch.txt");
write(&external_file_to_watch, "Hello world").unwrap();
let mut watch_arg = "--watch=".to_owned();
let external_file_to_watch_str = external_file_to_watch
.clone()
.into_os_string()
.into_string()
.unwrap();
watch_arg.push_str(&external_file_to_watch_str);
let mut child = util::deno_cmd()
.current_dir(util::testdata_path())
.arg("run")
.arg(watch_arg)
.arg("--unstable")
.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);
assert_contains!(stdout_lines.next().unwrap(), "Hello world");
wait_for("Process finished", &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);
check_alive_then_kill(child);
}
#[test]
fn run_watch_load_unload_events() {
let t = TempDir::new().unwrap();

View file

@ -239,6 +239,6 @@ pub fn compile_to_runtime_flags(
unstable: flags.unstable,
v8_flags: flags.v8_flags,
version: false,
watch: false,
watch: None,
})
}