mirror of
https://github.com/denoland/deno.git
synced 2025-03-04 01:44:26 -05:00
feat(repl): add --eval flag for evaluating code when the repl starts (#11590)
This commit is contained in:
parent
33c8d790c3
commit
864ce6e832
5 changed files with 133 additions and 15 deletions
44
cli/flags.rs
44
cli/flags.rs
|
@ -91,7 +91,9 @@ pub enum DenoSubcommand {
|
||||||
rules: bool,
|
rules: bool,
|
||||||
json: bool,
|
json: bool,
|
||||||
},
|
},
|
||||||
Repl,
|
Repl {
|
||||||
|
eval: Option<String>,
|
||||||
|
},
|
||||||
Run {
|
Run {
|
||||||
script: String,
|
script: String,
|
||||||
},
|
},
|
||||||
|
@ -119,7 +121,7 @@ pub enum DenoSubcommand {
|
||||||
|
|
||||||
impl Default for DenoSubcommand {
|
impl Default for DenoSubcommand {
|
||||||
fn default() -> 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> {
|
fn repl_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||||
runtime_args(SubCommand::with_name("repl"), false, true)
|
runtime_args(SubCommand::with_name("repl"), false, true)
|
||||||
.about("Read Eval Print Loop")
|
.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> {
|
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) {
|
fn repl_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
|
||||||
runtime_args_parse(flags, matches, false, true);
|
runtime_args_parse(flags, matches, false, true);
|
||||||
flags.repl = 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_net = Some(vec![]);
|
||||||
flags.allow_env = Some(vec![]);
|
flags.allow_env = Some(vec![]);
|
||||||
flags.allow_run = Some(vec![]);
|
flags.allow_run = Some(vec![]);
|
||||||
|
@ -2706,7 +2717,7 @@ mod tests {
|
||||||
r.unwrap(),
|
r.unwrap(),
|
||||||
Flags {
|
Flags {
|
||||||
repl: true,
|
repl: true,
|
||||||
subcommand: DenoSubcommand::Repl,
|
subcommand: DenoSubcommand::Repl { eval: None },
|
||||||
allow_net: Some(vec![]),
|
allow_net: Some(vec![]),
|
||||||
allow_env: Some(vec![]),
|
allow_env: Some(vec![]),
|
||||||
allow_run: Some(vec![]),
|
allow_run: Some(vec![]),
|
||||||
|
@ -2727,7 +2738,7 @@ mod tests {
|
||||||
r.unwrap(),
|
r.unwrap(),
|
||||||
Flags {
|
Flags {
|
||||||
repl: true,
|
repl: true,
|
||||||
subcommand: DenoSubcommand::Repl,
|
subcommand: DenoSubcommand::Repl { eval: None },
|
||||||
import_map_path: Some("import_map.json".to_string()),
|
import_map_path: Some("import_map.json".to_string()),
|
||||||
no_remote: true,
|
no_remote: true,
|
||||||
config_path: Some("tsconfig.json".to_string()),
|
config_path: Some("tsconfig.json".to_string()),
|
||||||
|
@ -2753,6 +2764,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]
|
#[test]
|
||||||
fn allow_read_allowlist() {
|
fn allow_read_allowlist() {
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
|
@ -774,7 +774,10 @@ async fn format_command(
|
||||||
Ok(())
|
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 main_module = resolve_url_or_path("./$deno$repl.ts").unwrap();
|
||||||
let permissions = Permissions::from_options(&flags.clone().into());
|
let permissions = Permissions::from_options(&flags.clone().into());
|
||||||
let program_state = ProgramState::build(flags).await?;
|
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);
|
create_main_worker(&program_state, main_module.clone(), permissions, false);
|
||||||
worker.run_event_loop(false).await?;
|
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> {
|
async fn run_from_stdin(flags: Flags) -> Result<(), AnyError> {
|
||||||
|
@ -1341,7 +1344,7 @@ fn get_subcommand(
|
||||||
ignore,
|
ignore,
|
||||||
json,
|
json,
|
||||||
} => lint_command(flags, files, rules, ignore, json).boxed_local(),
|
} => 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::Run { script } => run_command(flags, script).boxed_local(),
|
||||||
DenoSubcommand::Test {
|
DenoSubcommand::Test {
|
||||||
no_run,
|
no_run,
|
||||||
|
|
|
@ -436,7 +436,7 @@ fn exports_stripped() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_unterminated() {
|
fn call_eval_unterminated() {
|
||||||
let (out, err) = util::run_and_collect_output(
|
let (out, err) = util::run_and_collect_output(
|
||||||
true,
|
true,
|
||||||
"repl",
|
"repl",
|
||||||
|
@ -644,3 +644,45 @@ fn custom_inspect() {
|
||||||
assert!(out.contains("Oops custom inspect error"));
|
assert!(out.contains("Oops custom inspect error"));
|
||||||
assert!(err.is_empty());
|
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());
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
struct ReplSession {
|
||||||
worker: MainWorker,
|
worker: MainWorker,
|
||||||
session: LocalInspectorSession,
|
session: LocalInspectorSession,
|
||||||
|
@ -497,7 +511,7 @@ impl ReplSession {
|
||||||
pub async fn evaluate_line_and_get_output(
|
pub async fn evaluate_line_and_get_output(
|
||||||
&mut self,
|
&mut self,
|
||||||
line: &str,
|
line: &str,
|
||||||
) -> Result<String, AnyError> {
|
) -> Result<EvaluationOutput, AnyError> {
|
||||||
match self.evaluate_line_with_object_wrapping(line).await {
|
match self.evaluate_line_with_object_wrapping(line).await {
|
||||||
Ok(evaluate_response) => {
|
Ok(evaluate_response) => {
|
||||||
let evaluate_result = evaluate_response.get("result").unwrap();
|
let evaluate_result = evaluate_response.get("result").unwrap();
|
||||||
|
@ -512,20 +526,20 @@ impl ReplSession {
|
||||||
|
|
||||||
let value = self.get_eval_value(evaluate_result).await?;
|
let value = self.get_eval_value(evaluate_result).await?;
|
||||||
Ok(match evaluate_exception_details {
|
Ok(match evaluate_exception_details {
|
||||||
Some(_) => format!("Uncaught {}", value),
|
Some(_) => EvaluationOutput::Error(format!("Uncaught {}", value)),
|
||||||
None => value,
|
None => EvaluationOutput::Value(value),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// handle a parsing diagnostic
|
// handle a parsing diagnostic
|
||||||
match err.downcast_ref::<Diagnostic>() {
|
match err.downcast_ref::<Diagnostic>() {
|
||||||
Some(diagnostic) => Ok(format!(
|
Some(diagnostic) => Ok(EvaluationOutput::Error(format!(
|
||||||
"{}: {} at {}:{}",
|
"{}: {} at {}:{}",
|
||||||
colors::red("parse error"),
|
colors::red("parse error"),
|
||||||
diagnostic.message,
|
diagnostic.message,
|
||||||
diagnostic.location.line,
|
diagnostic.location.line,
|
||||||
diagnostic.location.col
|
diagnostic.location.col
|
||||||
)),
|
))),
|
||||||
None => Err(err),
|
None => Err(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -707,6 +721,7 @@ async fn read_line_and_poll(
|
||||||
pub async fn run(
|
pub async fn run(
|
||||||
program_state: &ProgramState,
|
program_state: &ProgramState,
|
||||||
worker: MainWorker,
|
worker: MainWorker,
|
||||||
|
maybe_eval: Option<String>,
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
let mut repl_session = ReplSession::initialize(worker).await?;
|
let mut repl_session = ReplSession::initialize(worker).await?;
|
||||||
let (message_tx, mut message_rx) = channel(1);
|
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 history_file_path = program_state.dir.root.join("deno_history.txt");
|
||||||
let editor = ReplEditor::new(helper, history_file_path);
|
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!("Deno {}", crate::version::deno());
|
||||||
println!("exit using ctrl+d or close()");
|
println!("exit using ctrl+d or close()");
|
||||||
|
|
||||||
|
|
|
@ -1142,10 +1142,26 @@ pub fn run_and_collect_output(
|
||||||
input: Option<Vec<&str>>,
|
input: Option<Vec<&str>>,
|
||||||
envs: Option<Vec<(String, String)>>,
|
envs: Option<Vec<(String, String)>>,
|
||||||
need_http_server: bool,
|
need_http_server: bool,
|
||||||
|
) -> (String, String) {
|
||||||
|
run_and_collect_output_with_args(
|
||||||
|
expect_success,
|
||||||
|
args.split_whitespace().collect(),
|
||||||
|
input,
|
||||||
|
envs,
|
||||||
|
need_http_server,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_and_collect_output_with_args(
|
||||||
|
expect_success: bool,
|
||||||
|
args: Vec<&str>,
|
||||||
|
input: Option<Vec<&str>>,
|
||||||
|
envs: Option<Vec<(String, String)>>,
|
||||||
|
need_http_server: bool,
|
||||||
) -> (String, String) {
|
) -> (String, String) {
|
||||||
let mut deno_process_builder = deno_cmd();
|
let mut deno_process_builder = deno_cmd();
|
||||||
deno_process_builder
|
deno_process_builder
|
||||||
.args(args.split_whitespace())
|
.args(args)
|
||||||
.current_dir(&tests_path())
|
.current_dir(&tests_path())
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
|
|
Loading…
Add table
Reference in a new issue