diff options
-rw-r--r-- | cli/flags.rs | 76 | ||||
-rw-r--r-- | cli/main.rs | 2 | ||||
-rw-r--r-- | cli/tests/integration/repl_tests.rs | 52 | ||||
-rw-r--r-- | cli/tools/repl/mod.rs | 35 |
4 files changed, 159 insertions, 6 deletions
diff --git a/cli/flags.rs b/cli/flags.rs index cdd90fa33..b0e4b32ed 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -144,6 +144,7 @@ pub struct LintFlags { #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct ReplFlags { + pub eval_files: Option<Vec<String>>, pub eval: Option<String>, } @@ -216,7 +217,10 @@ pub enum DenoSubcommand { impl Default for DenoSubcommand { fn default() -> DenoSubcommand { - DenoSubcommand::Repl(ReplFlags { eval: None }) + DenoSubcommand::Repl(ReplFlags { + eval_files: None, + eval: None, + }) } } @@ -556,7 +560,13 @@ where Some(("uninstall", m)) => uninstall_parse(&mut flags, m), Some(("upgrade", m)) => upgrade_parse(&mut flags, m), Some(("vendor", m)) => vendor_parse(&mut flags, m), - _ => handle_repl_flags(&mut flags, ReplFlags { eval: None }), + _ => handle_repl_flags( + &mut flags, + ReplFlags { + eval_files: None, + eval: None, + }, + ), } Ok(flags) @@ -1361,6 +1371,16 @@ fn repl_subcommand<'a>() -> Command<'a> { runtime_args(Command::new("repl"), false, true) .about("Read Eval Print Loop") .arg( + Arg::new("eval-file") + .long("eval-file") + .min_values(1) + .takes_value(true) + .use_value_delimiter(true) + .require_equals(true) + .help("Evaluates the provided file(s) as scripts when the REPL starts. Accepts file paths and URLs.") + .value_hint(ValueHint::AnyPath), + ) + .arg( Arg::new("eval") .long("eval") .help("Evaluates the provided code when the REPL starts.") @@ -2446,9 +2466,15 @@ fn repl_parse(flags: &mut Flags, matches: &clap::ArgMatches) { flags.type_check_mode = TypeCheckMode::None; runtime_args_parse(flags, matches, false, true); unsafely_ignore_certificate_errors_parse(flags, matches); + + let eval_files: Option<Vec<String>> = matches + .values_of("eval-file") + .map(|values| values.map(String::from).collect()); + handle_repl_flags( flags, ReplFlags { + eval_files, eval: matches.value_of("eval").map(ToOwned::to_owned), }, ); @@ -3884,7 +3910,10 @@ mod tests { r.unwrap(), Flags { repl: true, - subcommand: DenoSubcommand::Repl(ReplFlags { eval: None }), + subcommand: DenoSubcommand::Repl(ReplFlags { + eval_files: None, + eval: None + }), allow_net: Some(vec![]), unsafely_ignore_certificate_errors: None, allow_env: Some(vec![]), @@ -3906,7 +3935,10 @@ mod tests { r.unwrap(), Flags { repl: true, - subcommand: DenoSubcommand::Repl(ReplFlags { eval: None }), + subcommand: DenoSubcommand::Repl(ReplFlags { + eval_files: None, + eval: None + }), import_map_path: Some("import_map.json".to_string()), no_remote: true, config_path: Some("tsconfig.json".to_string()), @@ -3942,6 +3974,7 @@ mod tests { Flags { repl: true, subcommand: DenoSubcommand::Repl(ReplFlags { + eval_files: None, eval: Some("console.log('hello');".to_string()), }), allow_net: Some(vec![]), @@ -3958,6 +3991,35 @@ mod tests { } #[test] + fn repl_with_eval_file_flag() { + #[rustfmt::skip] + let r = flags_from_vec(svec!["deno", "repl", "--eval-file=./a.js,./b.ts,https://examples.deno.land/hello-world.ts"]); + assert_eq!( + r.unwrap(), + Flags { + repl: true, + subcommand: DenoSubcommand::Repl(ReplFlags { + eval_files: Some(vec![ + "./a.js".to_string(), + "./b.ts".to_string(), + "https://examples.deno.land/hello-world.ts".to_string() + ]), + eval: None, + }), + allow_net: Some(vec![]), + allow_env: Some(vec![]), + allow_run: Some(vec![]), + allow_read: Some(vec![]), + allow_write: Some(vec![]), + allow_ffi: Some(vec![]), + allow_hrtime: true, + type_check_mode: TypeCheckMode::None, + ..Flags::default() + } + ); + } + + #[test] fn allow_read_allowlist() { use test_util::TempDir; let temp_dir_guard = TempDir::new(); @@ -4590,6 +4652,7 @@ mod tests { Flags { repl: true, subcommand: DenoSubcommand::Repl(ReplFlags { + eval_files: None, eval: Some("console.log('hello');".to_string()), }), unsafely_ignore_certificate_errors: Some(vec![]), @@ -4663,7 +4726,10 @@ mod tests { r.unwrap(), Flags { repl: true, - subcommand: DenoSubcommand::Repl(ReplFlags { eval: None }), + subcommand: DenoSubcommand::Repl(ReplFlags { + eval_files: None, + eval: None + }), unsafely_ignore_certificate_errors: Some(svec![ "deno.land", "localhost", diff --git a/cli/main.rs b/cli/main.rs index 7f86967aa..218bc70f5 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -930,7 +930,7 @@ async fn repl_command( } worker.run_event_loop(false).await?; - tools::repl::run(&ps, worker, repl_flags.eval).await + tools::repl::run(&ps, worker, repl_flags.eval_files, repl_flags.eval).await } async fn run_from_stdin(flags: Flags) -> Result<i32, AnyError> { diff --git a/cli/tests/integration/repl_tests.rs b/cli/tests/integration/repl_tests.rs index 59acc7dfb..a63be3ece 100644 --- a/cli/tests/integration/repl_tests.rs +++ b/cli/tests/integration/repl_tests.rs @@ -709,3 +709,55 @@ fn eval_flag_runtime_error() { assert!(out.contains("2500")); // should not prevent input assert!(err.is_empty()); } + +#[test] +fn eval_file_flag_valid_input() { + let (out, err) = util::run_and_collect_output_with_args( + true, + vec!["repl", "--eval-file=./001_hello.js"], + None, + None, + false, + ); + assert!(out.contains("Hello World")); + assert!(err.is_empty()); +} + +#[test] +fn eval_file_flag_call_defined_function() { + let (out, err) = util::run_and_collect_output_with_args( + true, + vec!["repl", "--eval-file=./tsc/d.ts"], + Some(vec!["v4()"]), + None, + false, + ); + assert!(out.contains("hello")); + assert!(err.is_empty()); +} + +#[test] +fn eval_file_flag_http_input() { + let (out, err) = util::run_and_collect_output_with_args( + true, + vec!["repl", "--eval-file=http://127.0.0.1:4545/tsc/d.ts"], + Some(vec!["v4()"]), + None, + true, + ); + assert!(out.contains("hello")); + assert!(err.contains("Download")); +} + +#[test] +fn eval_file_flag_multiple_files() { + let (out, err) = util::run_and_collect_output_with_args( + true, + vec!["repl", "--eval-file=http://127.0.0.1:4545/import_type.ts,./tsc/d.ts,http://127.0.0.1:4545/type_definitions/foo.js"], + Some(vec!["b.method1=v4", "b.method1()+foo.toUpperCase()"]), + None, + true, + ); + assert!(out.contains("helloFOO")); + assert!(err.contains("Download")); +} diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs index b49c641c4..f0964ec4d 100644 --- a/cli/tools/repl/mod.rs +++ b/cli/tools/repl/mod.rs @@ -2,6 +2,7 @@ use crate::proc_state::ProcState; use deno_core::error::AnyError; +use deno_runtime::permissions::Permissions; use deno_runtime::worker::MainWorker; use rustyline::error::ReadlineError; @@ -58,9 +59,24 @@ async fn read_line_and_poll( } } +async fn read_eval_file( + ps: &ProcState, + eval_file: &str, +) -> Result<String, AnyError> { + let specifier = deno_core::resolve_url_or_path(eval_file)?; + + let file = ps + .file_fetcher + .fetch(&specifier, &mut Permissions::allow_all()) + .await?; + + Ok((*file.source).clone()) +} + pub async fn run( ps: &ProcState, worker: MainWorker, + maybe_eval_files: Option<Vec<String>>, maybe_eval: Option<String>, ) -> Result<i32, AnyError> { let mut repl_session = ReplSession::initialize(worker).await?; @@ -74,6 +90,25 @@ pub async fn run( let history_file_path = ps.dir.root.join("deno_history.txt"); let editor = ReplEditor::new(helper, history_file_path); + if let Some(eval_files) = maybe_eval_files { + for eval_file in eval_files { + match read_eval_file(ps, &eval_file).await { + Ok(eval_source) => { + let output = repl_session + .evaluate_line_and_get_output(&eval_source) + .await?; + // only output errors + if let EvaluationOutput::Error(error_text) = output { + println!("error in --eval-file file {}. {}", eval_file, error_text); + } + } + Err(e) => { + println!("error in --eval-file file {}. {}", eval_file, e); + } + } + } + } + if let Some(eval) = maybe_eval { let output = repl_session.evaluate_line_and_get_output(&eval).await?; // only output errors |