Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor CLI entry point #2157

Merged
merged 14 commits into from Apr 21, 2019

Large diffs are not rendered by default.

@@ -38,10 +38,13 @@ use crate::errors::RustOrJsError;
use crate::state::ThreadSafeState;
use crate::worker::root_specifier_to_url;
use crate::worker::Worker;
use deno::v8_set_flags;
use flags::{create_cli_app, DenoFlags};
use futures::lazy;
use futures::Future;
use log::{LevelFilter, Metadata, Record};
use std::env;
use std::path::Path;

static LOGGER: Logger = Logger;

@@ -123,103 +126,212 @@ pub fn print_file_info(worker: &Worker, url: &str) {
}
}

fn main() {
#[cfg(windows)]
ansi_term::enable_ansi_support().ok(); // For Windows 10
fn get_worker_and_state(
This conversation was marked as resolved by bartlomieju

This comment has been minimized.

Copy link
@ry

ry Apr 21, 2019

Collaborator

“get” doesn’t sound right here since the worker and state are being constructed. I’d prefer “create”

flags: DenoFlags,
rest_argv: Vec<String>,
) -> (Worker, ThreadSafeState) {
let state = ThreadSafeState::new(flags, rest_argv, ops::op_selector_std);
let worker = Worker::new(
"main".to_string(),
startup_data::deno_isolate_init(),
state.clone(),
);

log::set_logger(&LOGGER).unwrap();
let args = env::args().collect();
let (mut flags, mut rest_argv) =
flags::set_flags(args).unwrap_or_else(|err| {
eprintln!("{}", err);
std::process::exit(1)
});
(worker, state)
}

log::set_max_level(if flags.log_debug {
LevelFilter::Debug
} else {
LevelFilter::Warn
});
fn types_command() {
let p = Path::new(concat!(
env!("GN_OUT_DIR"),
"/gen/cli/lib/lib.deno_runtime.d.ts"
));
let content_bytes = std::fs::read(p).unwrap();
let content = std::str::from_utf8(&content_bytes[..]).unwrap();

if flags.fmt {
rest_argv.insert(1, "https://deno.land/std/prettier/main.ts".to_string());
flags.allow_read = true;
flags.allow_write = true;
}
println!("{}", content);

let should_prefetch = flags.prefetch || flags.info;
let should_display_info = flags.info;
std::process::exit(0);
}

let state = ThreadSafeState::new(flags, rest_argv, ops::op_selector_std);
let mut worker = Worker::new(
"main".to_string(),
startup_data::deno_isolate_init(),
state.clone(),
fn prefetch_or_info_command(
flags: DenoFlags,
argv: Vec<String>,
print_info: bool,
) {
let (mut worker, state) = get_worker_and_state(flags, argv);

let main_module = state.main_module().unwrap();
let main_future = lazy(move || {
// Setup runtime.
js_check(worker.execute("denoMain()"));
debug!("main_module {}", main_module);

let main_url = root_specifier_to_url(&main_module).unwrap();

worker
.execute_mod_async(&main_url, true)
.and_then(move |worker| {
if print_info {
print_file_info(&worker, &main_module);
}
worker.then(|result| {
js_check(result);
Ok(())
})
}).map_err(|(err, _worker)| print_err_and_exit(err))
});
tokio_util::run(main_future);
}

This comment has been minimized.

Copy link
@bartlomieju

bartlomieju Apr 20, 2019

Author Contributor

@ry PTAL at it. It seem that execute_mod_async does not evaluate and run file if prefetch is set to true, so simple letting worker finish the job seems to work fine. I'm not sure though


fn eval_command(flags: DenoFlags, argv: Vec<String>) {
let (mut worker, state) = get_worker_and_state(flags, argv);
// Wrap provided script in async function so asynchronous methods
// work. This is required until top-level await is not supported.
let js_source = format!(
"async function _topLevelWrapper(){{
{}
}}
_topLevelWrapper();
",
&state.argv[1]
);

// TODO(ry) somehow combine the two branches below. They're very similar but
// it's difficult to get the types to workout.

if state.flags.eval {
let main_future = lazy(move || {
js_check(worker.execute("denoMain()"));
// Wrap provided script in async function so asynchronous methods
// work. This is required until top-level await is not supported.
let js_source = format!(
"async function _topLevelWrapper(){{
{}
}}
_topLevelWrapper();
",
&state.argv[1]
);
// ATM imports in `deno eval` are not allowed
// TODO Support ES modules once Worker supports evaluating anonymous modules.
js_check(worker.execute(&js_source));
worker.then(|result| {
let main_future = lazy(move || {
js_check(worker.execute("denoMain()"));
// ATM imports in `deno eval` are not allowed
// TODO Support ES modules once Worker supports evaluating anonymous modules.
js_check(worker.execute(&js_source));
worker.then(|result| {
js_check(result);
Ok(())
})
});
tokio_util::run(main_future);
}

fn run_repl(flags: DenoFlags, argv: Vec<String>) {
let (mut worker, _state) = get_worker_and_state(flags, argv);

// REPL situation.
let main_future = lazy(move || {
// Setup runtime.
js_check(worker.execute("denoMain()"));
worker
.then(|result| {
js_check(result);
Ok(())
}).map_err(|(err, _worker): (RustOrJsError, Worker)| {
print_err_and_exit(err)
})
});
tokio_util::run(main_future);
} else if let Some(main_module) = state.main_module() {
// Normal situation of executing a module.

let main_future = lazy(move || {
// Setup runtime.
js_check(worker.execute("denoMain()"));
debug!("main_module {}", main_module);

let main_url = root_specifier_to_url(&main_module).unwrap();

worker
.execute_mod_async(&main_url, should_prefetch)
.and_then(move |worker| {
if should_display_info {
// Display file info and exit. Do not run file
print_file_info(&worker, &main_module);
std::process::exit(0);
}
worker.then(|result| {
js_check(result);
Ok(())
})
}).map_err(|(err, _worker)| print_err_and_exit(err))
});
tokio_util::run(main_future);
} else {
// REPL situation.
let main_future = lazy(move || {
// Setup runtime.
js_check(worker.execute("denoMain()"));
worker
.then(|result| {
});
tokio_util::run(main_future);
}

fn run_script(flags: DenoFlags, argv: Vec<String>) {
let (mut worker, state) = get_worker_and_state(flags, argv);

let main_module = state.main_module().unwrap();
// Normal situation of executing a module.
let main_future = lazy(move || {
// Setup runtime.
js_check(worker.execute("denoMain()"));
debug!("main_module {}", main_module);

let main_url = root_specifier_to_url(&main_module).unwrap();

worker
.execute_mod_async(&main_url, false)
.and_then(move |worker| {
worker.then(|result| {
js_check(result);
Ok(())
}).map_err(|(err, _worker): (RustOrJsError, Worker)| {
print_err_and_exit(err)
})
});
tokio_util::run(main_future);
}).map_err(|(err, _worker)| print_err_and_exit(err))
});
tokio_util::run(main_future);
}

fn fmt_command(mut flags: DenoFlags, mut argv: Vec<String>) {
argv.insert(1, "https://deno.land/std/prettier/main.ts".to_string());
flags.allow_read = true;
flags.allow_write = true;
run_script(flags, argv);
}

fn main() {
#[cfg(windows)]
ansi_term::enable_ansi_support().ok(); // For Windows 10

log::set_logger(&LOGGER).unwrap();
let args: Vec<String> = env::args().collect();
let cli_app = create_cli_app();
let matches = cli_app.get_matches_from(args);
let flags = flags::parse_flags(matches.clone());

if flags.v8_help {
// show v8 help and exit
// TODO(bartlomieju): this relies on `v8_set_flags` to swap `--v8-options` for "--help"
v8_set_flags(vec!["--v8-options".to_string()]);
This conversation was marked as resolved by bartlomieju

This comment has been minimized.

Copy link
@bartlomieju

bartlomieju Apr 20, 2019

Author Contributor

Other than this file only core/examples/http_bench.rs uses v8_set_flags. I guess we can now rid it of v8_set_flags_preprocess and remove this obscure swapping logic?

This comment has been minimized.

Copy link
@ry

ry Apr 20, 2019

Collaborator

@bartlomieju Nice clean ups. Getting rid v8_set_flags_preprocess seems good. Ping me when I should review again.

This comment has been minimized.

Copy link
@bartlomieju

bartlomieju Apr 21, 2019

Author Contributor

Thanks @ry. This is now ready for review.

}

match &flags.v8_flags {
Some(v8_flags) => {
v8_set_flags(v8_flags.clone());
}
_ => {}
};

log::set_max_level(if flags.log_debug {
LevelFilter::Debug
} else {
LevelFilter::Warn
});

let mut rest_argv: Vec<String> = vec!["deno".to_string()];
match matches.subcommand() {
("types", Some(_)) => {
types_command();
}
("eval", Some(info_match)) => {
let code: &str = info_match.value_of("code").unwrap();
rest_argv.extend(vec![code.to_string()]);
eval_command(flags, rest_argv);
}
("info", Some(info_match)) => {
let file: &str = info_match.value_of("file").unwrap();
rest_argv.extend(vec![file.to_string()]);
prefetch_or_info_command(flags, rest_argv, true);
}
("prefetch", Some(info_match)) => {
This conversation was marked as resolved by bartlomieju

This comment has been minimized.

Copy link
@ry

ry Apr 21, 2019

Collaborator

info_match should be prefetch_match

let file: &str = info_match.value_of("file").unwrap();
rest_argv.extend(vec![file.to_string()]);
prefetch_or_info_command(flags, rest_argv, false);
}
("fmt", Some(fmt_match)) => {
let files: Vec<String> = fmt_match
.values_of("files")
.unwrap()
.map(String::from)
.collect();
rest_argv.extend(files);
fmt_command(flags, rest_argv);
}
(script, Some(script_match)) => {
rest_argv.extend(vec![script.to_string()]);
// check if there are any extra arguments that should
// be passed to script
if script_match.is_present("") {
let script_args: Vec<String> = script_match
.values_of("")
.unwrap()
.map(String::from)
.collect();
rest_argv.extend(script_args);
}
run_script(flags, rest_argv);
}
_ => {
run_repl(flags, rest_argv);
}
}
}
@@ -321,7 +321,6 @@ fn op_start(
argv: Some(argv_off),
main_module,
debug_flag: state.flags.log_debug,
types_flag: state.flags.types,
version_flag: state.flags.version,
v8_version: Some(v8_version_off),
deno_version: Some(deno_version_off),
@@ -158,9 +158,11 @@ impl ThreadSafeState {
#[cfg(test)]
pub fn mock() -> ThreadSafeState {
let argv = vec![String::from("./deno"), String::from("hello.js")];
// For debugging: argv.push_back(String::from("-D"));
let (flags, rest_argv) = flags::set_flags(argv).unwrap();
ThreadSafeState::new(flags, rest_argv, ops::op_selector_std)
ThreadSafeState::new(
flags::DenoFlags::default(),
argv,
ops::op_selector_std,
)
}

pub fn metrics_op_dispatched(
@@ -267,9 +267,8 @@ mod tests {
let js_url = Url::from_file_path(filename).unwrap();

let argv = vec![String::from("./deno"), js_url.to_string()];
let (flags, rest_argv) = flags::set_flags(argv).unwrap();

let state = ThreadSafeState::new(flags, rest_argv, op_selector_std);
let state =
ThreadSafeState::new(flags::DenoFlags::default(), argv, op_selector_std);
let state_ = state.clone();
tokio_util::run(lazy(move || {
let worker = Worker::new("TEST".to_string(), StartupData::None, state);
@@ -294,9 +293,8 @@ mod tests {
let js_url = Url::from_file_path(filename).unwrap();

let argv = vec![String::from("./deno"), js_url.to_string()];
let (flags, rest_argv) = flags::set_flags(argv).unwrap();

let state = ThreadSafeState::new(flags, rest_argv, op_selector_std);
let state =
ThreadSafeState::new(flags::DenoFlags::default(), argv, op_selector_std);
let state_ = state.clone();
tokio_util::run(lazy(move || {
let worker = Worker::new("TEST".to_string(), StartupData::None, state);
@@ -15,9 +15,6 @@ import { setLocation } from "./location";
// builtin modules
import * as deno from "./deno";

// TODO(kitsonk) remove with `--types` below
import libDts from "gen/cli/lib/lib.deno_runtime.d.ts!string";

export default function denoMain(name?: string): void {
const startResMsg = os.start(name);

@@ -31,13 +28,6 @@ export default function denoMain(name?: string): void {
os.exit(0);
}

// handle `--types`
// TODO(kitsonk) move to Rust fetching from compiler
if (startResMsg.typesFlag()) {
console.log(libDts);
os.exit(0);
}

const mainModule = startResMsg.mainModule();
if (mainModule) {
assert(mainModule.length > 0);
@@ -1,2 +1,2 @@
args: --types
args: types
output: tests/types.out
@@ -11,7 +11,7 @@
# Builds into target/doc
run(["cargo", "doc", "--all", "--no-deps", "-vv"])

# 'deno --types' is stored in target/debug/gen/cli/lib/lib.deno_runtime.d.ts
# 'deno types' is stored in target/debug/gen/cli/lib/lib.deno_runtime.d.ts
# We want to run typedoc on that declaration file only.
os.chdir(os.path.join(target_path, "debug/gen/cli/lib/"))

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.