Skip to content

Commit

Permalink
Implement async module loading in CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
ry committed Apr 14, 2019
1 parent b413cd5 commit 32961a6
Show file tree
Hide file tree
Showing 16 changed files with 758 additions and 604 deletions.
43 changes: 43 additions & 0 deletions cli/deno_dir.rs
Expand Up @@ -853,6 +853,49 @@ fn save_source_code_headers(
}
}

// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier
// TODO(ry) Add tests.
// TODO(ry) Move this to core?
pub fn resolve_module2(
specifier: &str,
base: &str,
) -> Result<String, url::ParseError> {
// 1. Apply the URL parser to specifier. If the result is not failure, return
// the result.
// let specifier = parse_local_or_remote(specifier)?.to_string();
if let Ok(specifier_url) = Url::parse(specifier) {
return Ok(specifier_url.to_string());
}

// 2. If specifier does not start with the character U+002F SOLIDUS (/), the
// two-character sequence U+002E FULL STOP, U+002F SOLIDUS (./), or the
// three-character sequence U+002E FULL STOP, U+002E FULL STOP, U+002F
// SOLIDUS (../), return failure.
if !specifier.starts_with("/")
&& !specifier.starts_with("./")
&& !specifier.starts_with("../")
{
return Err(url::ParseError::RelativeUrlWithCannotBeABaseBase);
}

// 3. Return the result of applying the URL parser to specifier with base URL
// as the base URL.
let base_url = if base != "." {
// This branch is part of the spec.
Url::parse(base)?
} else {
// This branch isn't in the spec.
let cwd = std::env::current_dir().unwrap();
// Get file url of current directory, make sure it ends in "/".
let cwd_file_url =
Url::from_file_path(cwd).unwrap().as_str().to_string() + "/";
// Purposely reparse here:
Url::parse(&cwd_file_url)?
};
let u = base_url.join(&specifier)?;
Ok(u.to_string())
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
10 changes: 10 additions & 0 deletions cli/errors.rs
Expand Up @@ -217,3 +217,13 @@ impl fmt::Display for RustOrJsError {
}
}
}

// TODO(ry) This is ugly. They are essentially the same type.
impl From<deno::Either<DenoError>> for RustOrJsError {
fn from(e: deno::Either<DenoError>) -> Self {
match e {
deno::Either::JSError(err) => RustOrJsError::Js(err),
deno::Either::Other(err) => RustOrJsError::Rust(err),
}
}
}
120 changes: 93 additions & 27 deletions cli/main.rs
Expand Up @@ -20,7 +20,6 @@ mod global_timer;
mod http_body;
mod http_util;
pub mod js_errors;
pub mod modules;
pub mod msg;
pub mod msg_util;
pub mod ops;
Expand Down Expand Up @@ -74,6 +73,49 @@ where
}
}

// TODO(ry) Move this to main.rs
pub fn print_file_info(worker: &Worker, url: &str) {
let maybe_out =
worker::fetch_module_meta_data_and_maybe_compile(&worker.state, url, ".");
if let Err(err) = maybe_out {
println!("{}", err);
return;
}
let out = maybe_out.unwrap();

println!("{} {}", ansi::bold("local:".to_string()), &(out.filename));

println!(
"{} {}",
ansi::bold("type:".to_string()),
msg::enum_name_media_type(out.media_type)
);

if out.maybe_output_code_filename.is_some() {
println!(
"{} {}",
ansi::bold("compiled:".to_string()),
out.maybe_output_code_filename.as_ref().unwrap(),
);
}

if out.maybe_source_map_filename.is_some() {
println!(
"{} {}",
ansi::bold("map:".to_string()),
out.maybe_source_map_filename.as_ref().unwrap()
);
}

let deps = worker.modules.deps(&out.module_name);
println!("{}{}", ansi::bold("deps:\n".to_string()), deps.name);
if let Some(ref depsdeps) = deps.deps {
for d in depsdeps {
println!("{}", d);
}
}
}

fn main() {
#[cfg(windows)]
ansi_term::enable_ansi_support().ok(); // For Windows 10
Expand Down Expand Up @@ -102,17 +144,18 @@ fn main() {
let should_display_info = flags.info;

let state = ThreadSafeState::new(flags, rest_argv, ops::op_selector_std);
let mut main_worker = Worker::new(
let mut worker = Worker::new(
"main".to_string(),
startup_data::deno_isolate_init(),
state.clone(),
);

let main_future = lazy(move || {
// Setup runtime.
js_check(main_worker.execute("denoMain()"));
// 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 {
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!(
Expand All @@ -125,25 +168,48 @@ fn main() {
);
// ATM imports in `deno eval` are not allowed
// TODO Support ES modules once Worker supports evaluating anonymous modules.
js_check(main_worker.execute(&js_source));
} else {
// Execute main module.
if let Some(main_module) = state.main_module() {
debug!("main_module {}", main_module);
js_check(main_worker.execute_mod(&main_module, should_prefetch));
if should_display_info {
// Display file info and exit. Do not run file
main_worker.print_file_info(&main_module);
std::process::exit(0);
}
}
}

main_worker.then(|result| {
js_check(result);
Ok(())
})
});

tokio_util::run(main_future);
js_check(worker.execute(&js_source));
worker.then(|result| {
js_check(result);
Ok(())
})
});
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);
worker
.execute_mod_async(&main_module, 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| {
js_check(result);
Ok(())
}).map_err(|(err, _worker): (RustOrJsError, Worker)| {
print_err_and_exit(err)
})
});
tokio_util::run(main_future);
}
}

0 comments on commit 32961a6

Please sign in to comment.