diff options
Diffstat (limited to 'cli')
-rw-r--r-- | cli/flags.rs | 44 | ||||
-rw-r--r-- | cli/main.rs | 9 | ||||
-rw-r--r-- | cli/tests/integration/repl_tests.rs | 44 | ||||
-rw-r--r-- | cli/tools/repl.rs | 33 |
4 files changed, 116 insertions, 14 deletions
diff --git a/cli/flags.rs b/cli/flags.rs index f42dd771c..2371445c3 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -91,7 +91,9 @@ pub enum DenoSubcommand { rules: bool, json: bool, }, - Repl, + Repl { + eval: Option<String>, + }, Run { script: String, }, @@ -119,7 +121,7 @@ pub enum DenoSubcommand { impl Default for DenoSubcommand { fn default() -> DenoSubcommand { - DenoSubcommand::Repl + DenoSubcommand::Repl { eval: None } } } @@ -955,6 +957,13 @@ Ignore linting a file by adding an ignore comment at the top of the file: fn repl_subcommand<'a, 'b>() -> App<'a, 'b> { runtime_args(SubCommand::with_name("repl"), false, true) .about("Read Eval Print Loop") + .arg( + Arg::with_name("eval") + .long("eval") + .help("Evaluates the provided code when the REPL starts.") + .takes_value(true) + .value_name("code"), + ) } fn run_subcommand<'a, 'b>() -> App<'a, 'b> { @@ -1701,7 +1710,9 @@ fn lint_parse(flags: &mut Flags, matches: &clap::ArgMatches) { fn repl_parse(flags: &mut Flags, matches: &clap::ArgMatches) { runtime_args_parse(flags, matches, false, true); flags.repl = true; - flags.subcommand = DenoSubcommand::Repl; + flags.subcommand = DenoSubcommand::Repl { + eval: matches.value_of("eval").map(ToOwned::to_owned), + }; flags.allow_net = Some(vec![]); flags.allow_env = Some(vec![]); flags.allow_run = Some(vec![]); @@ -2706,7 +2717,7 @@ mod tests { r.unwrap(), Flags { repl: true, - subcommand: DenoSubcommand::Repl, + subcommand: DenoSubcommand::Repl { eval: None }, allow_net: Some(vec![]), allow_env: Some(vec![]), allow_run: Some(vec![]), @@ -2727,7 +2738,7 @@ mod tests { r.unwrap(), Flags { repl: true, - subcommand: DenoSubcommand::Repl, + subcommand: DenoSubcommand::Repl { eval: None }, import_map_path: Some("import_map.json".to_string()), no_remote: true, config_path: Some("tsconfig.json".to_string()), @@ -2754,6 +2765,29 @@ mod tests { } #[test] + fn repl_with_eval_flag() { + #[rustfmt::skip] + let r = flags_from_vec(svec!["deno", "repl", "--eval", "console.log('hello');"]); + assert_eq!( + r.unwrap(), + Flags { + repl: true, + subcommand: DenoSubcommand::Repl { + eval: Some("console.log('hello');".to_string()), + }, + allow_net: Some(vec![]), + allow_env: Some(vec![]), + allow_run: Some(vec![]), + allow_read: Some(vec![]), + allow_write: Some(vec![]), + allow_plugin: true, + allow_hrtime: true, + ..Flags::default() + } + ); + } + + #[test] fn allow_read_allowlist() { use tempfile::TempDir; let temp_dir = TempDir::new().expect("tempdir fail").path().to_path_buf(); diff --git a/cli/main.rs b/cli/main.rs index 77366ec12..7d375c0c4 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -774,7 +774,10 @@ async fn format_command( Ok(()) } -async fn run_repl(flags: Flags) -> Result<(), AnyError> { +async fn run_repl( + flags: Flags, + maybe_eval: Option<String>, +) -> Result<(), AnyError> { let main_module = resolve_url_or_path("./$deno$repl.ts").unwrap(); let permissions = Permissions::from_options(&flags.clone().into()); let program_state = ProgramState::build(flags).await?; @@ -782,7 +785,7 @@ async fn run_repl(flags: Flags) -> Result<(), AnyError> { create_main_worker(&program_state, main_module.clone(), permissions, false); worker.run_event_loop(false).await?; - tools::repl::run(&program_state, worker).await + tools::repl::run(&program_state, worker, maybe_eval).await } async fn run_from_stdin(flags: Flags) -> Result<(), AnyError> { @@ -1341,7 +1344,7 @@ fn get_subcommand( ignore, json, } => lint_command(flags, files, rules, ignore, json).boxed_local(), - DenoSubcommand::Repl => run_repl(flags).boxed_local(), + DenoSubcommand::Repl { eval } => run_repl(flags, eval).boxed_local(), DenoSubcommand::Run { script } => run_command(flags, script).boxed_local(), DenoSubcommand::Test { no_run, diff --git a/cli/tests/integration/repl_tests.rs b/cli/tests/integration/repl_tests.rs index b96b398ae..7ce91d406 100644 --- a/cli/tests/integration/repl_tests.rs +++ b/cli/tests/integration/repl_tests.rs @@ -436,7 +436,7 @@ fn exports_stripped() { } #[test] -fn eval_unterminated() { +fn call_eval_unterminated() { let (out, err) = util::run_and_collect_output( true, "repl", @@ -644,3 +644,45 @@ fn custom_inspect() { assert!(out.contains("Oops custom inspect error")); assert!(err.is_empty()); } + +#[test] +fn eval_flag_valid_input() { + let (out, err) = util::run_and_collect_output_with_args( + true, + vec!["repl", "--eval", "const t = 10;"], + Some(vec!["t * 500;"]), + None, + false, + ); + assert!(out.contains("5000")); + assert!(err.is_empty()); +} + +#[test] +fn eval_flag_parse_error() { + let (out, err) = util::run_and_collect_output_with_args( + true, + vec!["repl", "--eval", "const %"], + Some(vec!["250 * 10"]), + None, + false, + ); + assert!(test_util::strip_ansi_codes(&out) + .contains("error in --eval flag. parse error: Unexpected token `%`.")); + assert!(out.contains("2500")); // should not prevent input + assert!(err.is_empty()); +} + +#[test] +fn eval_flag_runtime_error() { + let (out, err) = util::run_and_collect_output_with_args( + true, + vec!["repl", "--eval", "throw new Error('Testing')"], + Some(vec!["250 * 10"]), + None, + false, + ); + assert!(out.contains("error in --eval flag. Uncaught Error: Testing")); + assert!(out.contains("2500")); // should not prevent input + assert!(err.is_empty()); +} diff --git a/cli/tools/repl.rs b/cli/tools/repl.rs index d527ea868..3de859698 100644 --- a/cli/tools/repl.rs +++ b/cli/tools/repl.rs @@ -418,6 +418,20 @@ Object.defineProperty(globalThis, "_error", { }); "#; +enum EvaluationOutput { + Value(String), + Error(String), +} + +impl std::fmt::Display for EvaluationOutput { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + EvaluationOutput::Value(value) => f.write_str(value), + EvaluationOutput::Error(value) => f.write_str(value), + } + } +} + struct ReplSession { worker: MainWorker, session: LocalInspectorSession, @@ -497,7 +511,7 @@ impl ReplSession { pub async fn evaluate_line_and_get_output( &mut self, line: &str, - ) -> Result<String, AnyError> { + ) -> Result<EvaluationOutput, AnyError> { match self.evaluate_line_with_object_wrapping(line).await { Ok(evaluate_response) => { let evaluate_result = evaluate_response.get("result").unwrap(); @@ -512,20 +526,20 @@ impl ReplSession { let value = self.get_eval_value(evaluate_result).await?; Ok(match evaluate_exception_details { - Some(_) => format!("Uncaught {}", value), - None => value, + Some(_) => EvaluationOutput::Error(format!("Uncaught {}", value)), + None => EvaluationOutput::Value(value), }) } Err(err) => { // handle a parsing diagnostic match err.downcast_ref::<Diagnostic>() { - Some(diagnostic) => Ok(format!( + Some(diagnostic) => Ok(EvaluationOutput::Error(format!( "{}: {} at {}:{}", colors::red("parse error"), diagnostic.message, diagnostic.location.line, diagnostic.location.col - )), + ))), None => Err(err), } } @@ -707,6 +721,7 @@ async fn read_line_and_poll( pub async fn run( program_state: &ProgramState, worker: MainWorker, + maybe_eval: Option<String>, ) -> Result<(), AnyError> { let mut repl_session = ReplSession::initialize(worker).await?; let (message_tx, mut message_rx) = channel(1); @@ -721,6 +736,14 @@ pub async fn run( let history_file_path = program_state.dir.root.join("deno_history.txt"); let editor = ReplEditor::new(helper, history_file_path); + if let Some(eval) = maybe_eval { + let output = repl_session.evaluate_line_and_get_output(&eval).await?; + // only output errors + if let EvaluationOutput::Error(error_text) = output { + println!("error in --eval flag. {}", error_text); + } + } + println!("Deno {}", crate::version::deno()); println!("exit using ctrl+d or close()"); |