diff options
author | Ryan Dahl <ry@tinyclouds.org> | 2019-05-20 12:06:57 -0400 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2019-05-29 07:53:39 -0400 |
commit | 856c44213b7faf507d4b481cfc170b2fd08f971a (patch) | |
tree | b2971883b0aeb43437a9be0076b4ffacde55d5b8 /cli | |
parent | 64d2b7bc90cf6fdba661d6d3fe243fe332c076ee (diff) |
TS compiler refactor
* Compiler no longer has its own Tokio runtime. Compiler handles one
message and then exits.
* Uses the simpler ts.CompilerHost interface instead of
ts.LanguageServiceHost.
* avoids recompiling the same module by introducing a hacky but simple
`hashset<string>` that stores the module names that have been already
compiled.
* Removes the CompilerConfig op.
* Removes a lot of the mocking stuff in compiler.ts like `this._ts`. It
is not useful as we don't even have tests.
* Turns off checkJs because it causes fmt_test to die with OOM.
Diffstat (limited to 'cli')
-rw-r--r-- | cli/compiler.rs | 283 | ||||
-rw-r--r-- | cli/msg.fbs | 32 | ||||
-rw-r--r-- | cli/ops.rs | 106 | ||||
-rw-r--r-- | cli/state.rs | 18 | ||||
-rw-r--r-- | cli/worker.rs | 88 |
5 files changed, 220 insertions, 307 deletions
diff --git a/cli/compiler.rs b/cli/compiler.rs index d4913a4e2..e1bb56130 100644 --- a/cli/compiler.rs +++ b/cli/compiler.rs @@ -1,10 +1,6 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use crate::js_errors; -use crate::js_errors::JSErrorColor; use crate::msg; -use crate::ops::op_selector_compiler; use crate::resources; -use crate::resources::ResourceId; use crate::startup_data; use crate::state::*; use crate::tokio_util; @@ -12,31 +8,10 @@ use crate::worker::Worker; use deno::js_check; use deno::Buf; use deno::JSError; -use futures::future::*; -use futures::sync::oneshot; use futures::Future; use futures::Stream; -use serde_json; -use std::collections::HashMap; use std::str; -use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; -use std::sync::Mutex; -use tokio::runtime::Runtime; - -type CmdId = u32; -type ResponseSenderTable = HashMap<CmdId, oneshot::Sender<Buf>>; - -lazy_static! { - static ref C_NEXT_CMD_ID: AtomicUsize = AtomicUsize::new(1); - // Map of response senders - static ref C_RES_SENDER_TABLE: Mutex<ResponseSenderTable> = Mutex::new(ResponseSenderTable::new()); - // Shared worker resources so we can spawn - static ref C_RID: Mutex<Option<ResourceId>> = Mutex::new(None); - // tokio runtime specifically for spawning logic that is dependent on - // completetion of the compiler worker future - static ref C_RUNTIME: Mutex<Runtime> = Mutex::new(tokio_util::create_threadpool_runtime()); -} // This corresponds to JS ModuleMetaData. // TODO Rename one or the other so they correspond. @@ -72,91 +47,22 @@ impl ModuleMetaData { } } -fn new_cmd_id() -> CmdId { - let next_rid = C_NEXT_CMD_ID.fetch_add(1, Ordering::SeqCst); - next_rid as CmdId -} - -fn parse_cmd_id(res_json: &str) -> CmdId { - match serde_json::from_str::<serde_json::Value>(res_json) { - Ok(serde_json::Value::Object(map)) => match map["cmdId"].as_u64() { - Some(cmd_id) => cmd_id as CmdId, - _ => panic!("Error decoding compiler response: expected cmdId"), - }, - _ => panic!("Error decoding compiler response"), - } -} - -fn lazy_start(parent_state: ThreadSafeState) -> ResourceId { - let mut cell = C_RID.lock().unwrap(); - cell - .get_or_insert_with(|| { - let child_state = ThreadSafeState::new( - parent_state.flags.clone(), - parent_state.argv.clone(), - op_selector_compiler, - parent_state.progress.clone(), - ); - let rid = child_state.resource.rid; - let resource = child_state.resource.clone(); - - let mut worker = Worker::new( - "TS".to_string(), - startup_data::compiler_isolate_init(), - child_state, - ); - - js_check(worker.execute("denoMain()")); - js_check(worker.execute("workerMain()")); - js_check(worker.execute("compilerMain()")); +type CompilerConfig = Option<(String, Vec<u8>)>; - let mut runtime = C_RUNTIME.lock().unwrap(); - runtime.spawn(lazy(move || { - worker.then(move |result| -> Result<(), ()> { - // Close resource so the future created by - // handle_worker_message_stream exits - resource.close(); - debug!("Compiler worker exited!"); - if let Err(e) = result { - eprintln!("{}", JSErrorColor(&e).to_string()); - } - std::process::exit(1); - }) - })); - runtime.spawn(lazy(move || { - debug!("Start worker stream handler!"); - let worker_stream = resources::get_message_stream_from_worker(rid); - worker_stream - .for_each(|msg: Buf| { - // All worker responses are handled here first before being sent via - // their respective sender. This system can be compared to the - // promise system used on the js side. This provides a way to - // resolve many futures via the same channel. - let res_json = std::str::from_utf8(&msg).unwrap(); - debug!("Got message from worker: {}", res_json); - // Get the intended receiver's cmd_id from the message. - let cmd_id = parse_cmd_id(res_json); - let mut table = C_RES_SENDER_TABLE.lock().unwrap(); - debug!("Cmd id for get message handler: {}", cmd_id); - // Get the corresponding response sender from the table and - // send a response. - let response_sender = table.remove(&(cmd_id as CmdId)).unwrap(); - response_sender.send(msg).unwrap(); - Ok(()) - }).map_err(|_| ()) - })); - rid - }).to_owned() -} - -fn req(specifier: &str, referrer: &str, cmd_id: u32) -> Buf { - json!({ - "specifier": specifier, - "referrer": referrer, - "cmdId": cmd_id, - }).to_string() - .into_boxed_str() - .into_boxed_bytes() +/// Creates the JSON message send to compiler.ts's onmessage. +fn req(root_names: Vec<String>, compiler_config: CompilerConfig) -> Buf { + let j = if let Some((config_path, config_data)) = compiler_config { + json!({ + "rootNames": root_names, + "configPath": config_path, + "config": str::from_utf8(&config_data).unwrap(), + }) + } else { + json!({ + "rootNames": root_names, + }) + }; + j.to_string().into_boxed_str().into_boxed_bytes() } /// Returns an optional tuple which represents the state of the compiler @@ -165,7 +71,7 @@ fn req(specifier: &str, referrer: &str, cmd_id: u32) -> Buf { pub fn get_compiler_config( parent_state: &ThreadSafeState, _compiler_type: &str, -) -> Option<(String, Vec<u8>)> { +) -> CompilerConfig { // The compiler type is being passed to make it easier to implement custom // compilers in the future. match (&parent_state.config_path, &parent_state.config) { @@ -177,7 +83,7 @@ pub fn get_compiler_config( } pub fn compile_async( - parent_state: ThreadSafeState, + state: ThreadSafeState, specifier: &str, referrer: &str, module_meta_data: &ModuleMetaData, @@ -186,100 +92,86 @@ pub fn compile_async( "Running rust part of compile_sync. specifier: {}, referrer: {}", &specifier, &referrer ); - let cmd_id = new_cmd_id(); - let req_msg = req(&specifier, &referrer, cmd_id); + let root_names = vec![module_meta_data.module_name.clone()]; + let compiler_config = get_compiler_config(&state, "typescript"); + let req_msg = req(root_names, compiler_config); + let module_meta_data_ = module_meta_data.clone(); - let compiler_rid = lazy_start(parent_state.clone()); + // Count how many times we start the compiler worker. + state.metrics.compiler_starts.fetch_add(1, Ordering::SeqCst); + + let mut worker = Worker::new( + "TS".to_string(), + startup_data::compiler_isolate_init(), + // TODO(ry) Maybe we should use a separate state for the compiler. + // as was done previously. + state.clone(), + ); + js_check(worker.execute("denoMain()")); + js_check(worker.execute("workerMain()")); + js_check(worker.execute("compilerMain()")); - let compiling_job = parent_state + let compiling_job = state .progress .add(format!("Compiling {}", module_meta_data_.module_name)); - let (local_sender, local_receiver) = - oneshot::channel::<Result<ModuleMetaData, Option<JSError>>>(); - - let (response_sender, response_receiver) = oneshot::channel::<Buf>(); - - // Scoping to auto dispose of locks when done using them - { - let mut table = C_RES_SENDER_TABLE.lock().unwrap(); - debug!("Cmd id for response sender insert: {}", cmd_id); - // Place our response sender in the table so we can find it later. - table.insert(cmd_id, response_sender); - - let mut runtime = C_RUNTIME.lock().unwrap(); - runtime.spawn(lazy(move || { - resources::post_message_to_worker(compiler_rid, req_msg) - .then(move |_| { - debug!("Sent message to worker"); - response_receiver.map_err(|_| None) - }).and_then(move |res_msg| { - debug!("Received message from worker"); - let res_json = std::str::from_utf8(res_msg.as_ref()).unwrap(); - let res = serde_json::from_str::<serde_json::Value>(res_json) - .expect("Error decoding compiler response"); - let res_data = res["data"].as_object().expect( - "Error decoding compiler response: expected object field 'data'", - ); + let resource = worker.state.resource.clone(); + let compiler_rid = resource.rid; + let first_msg_fut = resources::post_message_to_worker(compiler_rid, req_msg) + .then(move |_| worker) + .then(move |result| { + if let Err(err) = result { + // TODO(ry) Need to forward the error instead of exiting. + eprintln!("{}", err.to_string()); + std::process::exit(1); + } + debug!("Sent message to worker"); + let stream_future = + resources::get_message_stream_from_worker(compiler_rid).into_future(); + stream_future.map(|(f, _rest)| f).map_err(|(f, _rest)| f) + }); + + first_msg_fut + .map_err(|_| panic!("not handled")) + .and_then(move |maybe_msg: Option<Buf>| { + let _res_msg = maybe_msg.unwrap(); + + debug!("Received message from worker"); + + // TODO res is EmitResult, use serde_derive to parse it. Errors from the + // worker or Diagnostics should be somehow forwarded to the caller! + // Currently they are handled inside compiler.ts with os.exit(1) and above + // with std::process::exit(1). This bad. + + let r = state.dir.fetch_module_meta_data( + &module_meta_data_.module_name, + ".", + true, + true, + ); + let module_meta_data_after_compile = r.unwrap(); - // Explicit drop to keep reference alive until future completes. - drop(compiling_job); + // Explicit drop to keep reference alive until future completes. + drop(compiling_job); - match res["success"].as_bool() { - Some(true) => Ok(ModuleMetaData { - maybe_output_code: res_data["outputCode"] - .as_str() - .map(|s| s.as_bytes().to_owned()), - maybe_source_map: res_data["sourceMap"] - .as_str() - .map(|s| s.as_bytes().to_owned()), - ..module_meta_data_ - }), - Some(false) => { - let js_error = JSError::from_json_value( - serde_json::Value::Object(res_data.clone()), - ).expect( - "Error decoding compiler response: failed to parse error", - ); - Err(Some(js_errors::apply_source_map( - &js_error, - &parent_state.dir, - ))) - } - _ => panic!( - "Error decoding compiler response: expected bool field 'success'" - ), - } - }).then(move |result| { - local_sender.send(result).expect("Oneshot send() failed"); - Ok(()) - }) - })); - } - - local_receiver - .map_err(|e| { - panic!( - "Local channel canceled before compile request could be completed: {}", - e - ) - }).and_then(move |result| match result { - Ok(v) => futures::future::result(Ok(v)), - Err(Some(err)) => futures::future::result(Err(err)), - Err(None) => panic!("Failed to communicate with the compiler worker."), + Ok(module_meta_data_after_compile) + }).then(move |r| { + // TODO(ry) do this in worker's destructor. + // resource.close(); + r }) } pub fn compile_sync( - parent_state: ThreadSafeState, + state: ThreadSafeState, specifier: &str, referrer: &str, module_meta_data: &ModuleMetaData, ) -> Result<ModuleMetaData, JSError> { tokio_util::block_on(compile_async( - parent_state, + state, specifier, referrer, module_meta_data, @@ -298,9 +190,13 @@ mod tests { let specifier = "./tests/002_hello.ts"; let referrer = cwd_string + "/"; + use crate::worker; + let module_name = worker::root_specifier_to_url(specifier) + .unwrap() + .to_string(); let mut out = ModuleMetaData { - module_name: "xxx".to_owned(), + module_name, module_redirect_source_name: None, filename: "/tests/002_hello.ts".to_owned(), media_type: msg::MediaType::TypeScript, @@ -323,17 +219,6 @@ mod tests { } #[test] - fn test_parse_cmd_id() { - let cmd_id = new_cmd_id(); - - let msg = req("Hello", "World", cmd_id); - - let res_json = std::str::from_utf8(&msg).unwrap(); - - assert_eq!(parse_cmd_id(res_json), cmd_id); - } - - #[test] fn test_get_compiler_config_no_flag() { let compiler_type = "typescript"; let state = ThreadSafeState::mock(); diff --git a/cli/msg.fbs b/cli/msg.fbs index 08e58351c..7f2db381f 100644 --- a/cli/msg.fbs +++ b/cli/msg.fbs @@ -1,12 +1,13 @@ union Any { Accept, + Cache, Chdir, Chmod, Chown, Close, - CompilerConfig, - CompilerConfigRes, CopyFile, + CreateWorker, + CreateWorkerRes, Cwd, CwdRes, Dial, @@ -23,6 +24,10 @@ union Any { GlobalTimer, GlobalTimerRes, GlobalTimerStop, + HostGetMessage, + HostGetMessageRes, + HostGetWorkerClosed, + HostPostMessage, IsTTY, IsTTYRes, Kill, @@ -70,12 +75,6 @@ union Any { Symlink, Truncate, Utime, - CreateWorker, - CreateWorkerRes, - HostGetWorkerClosed, - HostGetMessage, - HostGetMessageRes, - HostPostMessage, WorkerGetMessage, WorkerGetMessageRes, WorkerPostMessage, @@ -180,15 +179,6 @@ table StartRes { xeval_delim: string; } -table CompilerConfig { - compiler_type: string; -} - -table CompilerConfigRes { - path: string; - data: [ubyte]; -} - table FormatError { error: string; } @@ -246,7 +236,7 @@ table FetchModuleMetaData { table FetchModuleMetaDataRes { // If it's a non-http module, moduleName and filename will be the same. - // For http modules, moduleName is its resolved http URL, and filename + // For http modules, module_name is its resolved http URL, and filename // is the location of the locally downloaded source code. module_name: string; filename: string; @@ -254,6 +244,12 @@ table FetchModuleMetaDataRes { data: [ubyte]; } +table Cache { + extension: string; + module_id: string; + contents: string; +} + table Chdir { directory: string; } diff --git a/cli/ops.rs b/cli/ops.rs index 1f208bbe2..e41c9caa2 100644 --- a/cli/ops.rs +++ b/cli/ops.rs @@ -1,7 +1,6 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. use atty; use crate::ansi; -use crate::compiler::get_compiler_config; use crate::deno_dir::resolve_path; use crate::dispatch_minimal::dispatch_minimal; use crate::dispatch_minimal::parse_min_record; @@ -177,37 +176,30 @@ pub fn dispatch_all_legacy( } } -pub fn op_selector_compiler(inner_type: msg::Any) -> Option<OpCreator> { - match inner_type { - msg::Any::CompilerConfig => Some(op_compiler_config), - msg::Any::Cwd => Some(op_cwd), - msg::Any::FetchModuleMetaData => Some(op_fetch_module_meta_data), - msg::Any::WorkerGetMessage => Some(op_worker_get_message), - msg::Any::WorkerPostMessage => Some(op_worker_post_message), - msg::Any::Exit => Some(op_exit), - msg::Any::Start => Some(op_start), - _ => None, - } -} - /// Standard ops set for most isolates pub fn op_selector_std(inner_type: msg::Any) -> Option<OpCreator> { match inner_type { msg::Any::Accept => Some(op_accept), + msg::Any::Cache => Some(op_cache), msg::Any::Chdir => Some(op_chdir), msg::Any::Chmod => Some(op_chmod), msg::Any::Chown => Some(op_chown), msg::Any::Close => Some(op_close), msg::Any::CopyFile => Some(op_copy_file), + msg::Any::CreateWorker => Some(op_create_worker), msg::Any::Cwd => Some(op_cwd), msg::Any::Dial => Some(op_dial), msg::Any::Environ => Some(op_env), msg::Any::Exit => Some(op_exit), msg::Any::Fetch => Some(op_fetch), + msg::Any::FetchModuleMetaData => Some(op_fetch_module_meta_data), msg::Any::FormatError => Some(op_format_error), msg::Any::GetRandomValues => Some(op_get_random_values), msg::Any::GlobalTimer => Some(op_global_timer), msg::Any::GlobalTimerStop => Some(op_global_timer_stop), + msg::Any::HostGetMessage => Some(op_host_get_message), + msg::Any::HostGetWorkerClosed => Some(op_host_get_worker_closed), + msg::Any::HostPostMessage => Some(op_host_post_message), msg::Any::IsTTY => Some(op_is_tty), msg::Any::Kill => Some(op_kill), msg::Any::Link => Some(op_link), @@ -237,10 +229,6 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option<OpCreator> { msg::Any::Symlink => Some(op_symlink), msg::Any::Truncate => Some(op_truncate), msg::Any::Utime => Some(op_utime), - msg::Any::CreateWorker => Some(op_create_worker), - msg::Any::HostGetWorkerClosed => Some(op_host_get_worker_closed), - msg::Any::HostGetMessage => Some(op_host_get_message), - msg::Any::HostPostMessage => Some(op_host_post_message), msg::Any::Write => Some(op_write), // TODO(ry) split these out so that only the appropriate Workers can access @@ -446,6 +434,53 @@ pub fn odd_future(err: DenoError) -> Box<OpWithError> { Box::new(futures::future::err(err)) } +fn op_cache( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> Box<OpWithError> { + assert!(data.is_none()); + let inner = base.inner_as_cache().unwrap(); + let extension = inner.extension().unwrap(); + let module_id = inner.module_id().unwrap(); + let contents = inner.contents().unwrap(); + + state.mark_compiled(&module_id); + + // TODO It shouldn't be necessary to call fetch_module_meta_data() here. + // However, we need module_meta_data.source_code in order to calculate the + // cache path. In the future, checksums will not be used in the cache + // filenames and this requirement can be removed. See + // https://github.com/denoland/deno/issues/2057 + let r = state.dir.fetch_module_meta_data(module_id, ".", true, true); + if let Err(err) = r { + return odd_future(err); + } + let module_meta_data = r.unwrap(); + + let (js_cache_path, source_map_path) = state + .dir + .cache_path(&module_meta_data.filename, &module_meta_data.source_code); + + if extension == ".map" { + debug!("cache {:?}", source_map_path); + let r = fs::write(source_map_path, contents); + if let Err(err) = r { + return odd_future(err.into()); + } + } else if extension == ".js" { + debug!("cache {:?}", js_cache_path); + let r = fs::write(js_cache_path, contents); + if let Err(err) = r { + return odd_future(err.into()); + } + } else { + unreachable!(); + } + + ok_future(empty_buf()) +} + // https://github.com/denoland/deno/blob/golang/os.go#L100-L154 fn op_fetch_module_meta_data( state: &ThreadSafeState, @@ -498,41 +533,6 @@ fn op_fetch_module_meta_data( Box::new(futures::future::result(tokio_util::block_on(fut))) } -/// Retrieve any relevant compiler configuration. -fn op_compiler_config( - state: &ThreadSafeState, - base: &msg::Base<'_>, - data: Option<PinnedBuf>, -) -> Box<OpWithError> { - assert!(data.is_none()); - let inner = base.inner_as_compiler_config().unwrap(); - let cmd_id = base.cmd_id(); - let compiler_type = inner.compiler_type().unwrap(); - - Box::new(futures::future::result(|| -> OpResult { - let builder = &mut FlatBufferBuilder::new(); - let (path, out) = match get_compiler_config(state, compiler_type) { - Some(val) => val, - _ => ("".to_owned(), vec![]), - }; - let data_off = builder.create_vector(&out); - let msg_args = msg::CompilerConfigResArgs { - path: Some(builder.create_string(&path)), - data: Some(data_off), - }; - let inner = msg::CompilerConfigRes::create(builder, &msg_args); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::CompilerConfigRes, - ..Default::default() - }, - )) - }())) -} - fn op_chdir( _state: &ThreadSafeState, base: &msg::Base<'_>, diff --git a/cli/state.rs b/cli/state.rs index 2254f8e25..dffd1202f 100644 --- a/cli/state.rs +++ b/cli/state.rs @@ -15,6 +15,7 @@ use deno::PinnedBuf; use futures::future::Shared; use std; use std::collections::HashMap; +use std::collections::HashSet; use std::env; use std::fs; use std::ops::Deref; @@ -37,6 +38,7 @@ pub struct Metrics { pub bytes_sent_data: AtomicUsize, pub bytes_received: AtomicUsize, pub resolve_count: AtomicUsize, + pub compiler_starts: AtomicUsize, } /// Isolate cannot be passed between threads but ThreadSafeState can. @@ -66,6 +68,11 @@ pub struct State { pub dispatch_selector: ops::OpSelector, /// Reference to global progress bar. pub progress: Progress, + + /// Set of all URLs that have been compiled. This is a hacky way to work + /// around the fact that --reload will force multiple compilations of the same + /// module. + compiled: Mutex<HashSet<String>>, } impl Clone for ThreadSafeState { @@ -157,6 +164,7 @@ impl ThreadSafeState { resource, dispatch_selector, progress, + compiled: Mutex::new(HashSet::new()), })) } @@ -177,6 +185,16 @@ impl ThreadSafeState { } } + pub fn mark_compiled(&self, module_id: &str) { + let mut c = self.compiled.lock().unwrap(); + c.insert(module_id.to_string()); + } + + pub fn has_compiled(&self, module_id: &str) -> bool { + let c = self.compiled.lock().unwrap(); + c.contains(module_id) + } + #[inline] pub fn check_read(&self, filename: &str) -> DenoResult<()> { self.permissions.check_read(filename) diff --git a/cli/worker.rs b/cli/worker.rs index 98bea6eb8..59eecda6f 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -212,35 +212,39 @@ fn fetch_module_meta_data_and_maybe_compile_async( specifier: &str, referrer: &str, ) -> impl Future<Item = ModuleMetaData, Error = DenoError> { - let use_cache = !state.flags.reload; - let no_fetch = state.flags.no_fetch; let state_ = state.clone(); let specifier = specifier.to_string(); let referrer = referrer.to_string(); - state - .dir - .fetch_module_meta_data_async(&specifier, &referrer, use_cache, no_fetch) - .and_then(move |out| { - if out.media_type == msg::MediaType::TypeScript - && !out.has_output_code_and_source_map() - { - debug!(">>>>> compile_sync START"); - Either::A( - compile_async(state_.clone(), &specifier, &referrer, &out) - .map_err(|e| { - debug!("compiler error exiting!"); - eprintln!("{}", JSErrorColor(&e).to_string()); - std::process::exit(1); - }).and_then(move |out| { - debug!(">>>>> compile_sync END"); - state_.dir.code_cache(&out)?; - Ok(out) - }), - ) - } else { - Either::B(futures::future::ok(out)) - } - }) + + let f = futures::future::result(Worker::resolve(&specifier, &referrer)); + f.and_then(move |module_id| { + let use_cache = !state_.flags.reload || state_.has_compiled(&module_id); + let no_fetch = state_.flags.no_fetch; + + state_ + .dir + .fetch_module_meta_data_async(&specifier, &referrer, use_cache, no_fetch) + .and_then(move |out| { + if out.media_type == msg::MediaType::TypeScript + && !out.has_output_code_and_source_map() + { + debug!(">>>>> compile_sync START"); + Either::A( + compile_async(state_.clone(), &specifier, &referrer, &out) + .map_err(|e| { + debug!("compiler error exiting!"); + eprintln!("{}", JSErrorColor(&e).to_string()); + std::process::exit(1); + }).and_then(move |out| { + debug!(">>>>> compile_sync END"); + Ok(out) + }), + ) + } else { + Either::B(futures::future::ok(out)) + } + }) + }) } pub fn fetch_module_meta_data_and_maybe_compile( @@ -297,6 +301,8 @@ mod tests { let metrics = &state_.metrics; assert_eq!(metrics.resolve_count.load(Ordering::SeqCst), 2); + // Check that we didn't start the compiler. + assert_eq!(metrics.compiler_starts.load(Ordering::SeqCst), 0); } #[test] @@ -327,6 +333,8 @@ mod tests { let metrics = &state_.metrics; assert_eq!(metrics.resolve_count.load(Ordering::SeqCst), 2); + // Check that we didn't start the compiler. + assert_eq!(metrics.compiler_starts.load(Ordering::SeqCst), 0); } #[test] @@ -361,6 +369,8 @@ mod tests { let metrics = &state_.metrics; assert_eq!(metrics.resolve_count.load(Ordering::SeqCst), 3); + // Check that we've only invoked the compiler once. + assert_eq!(metrics.compiler_starts.load(Ordering::SeqCst), 1); } fn create_test_worker() -> Worker { @@ -459,20 +469,24 @@ mod tests { #[test] fn execute_mod_resolve_error() { - // "foo" is not a vailid module specifier so this should return an error. - let worker = create_test_worker(); - let js_url = root_specifier_to_url("does-not-exist").unwrap(); - let result = worker.execute_mod_async(&js_url, false).wait(); - assert!(result.is_err()); + tokio_util::init(|| { + // "foo" is not a vailid module specifier so this should return an error. + let worker = create_test_worker(); + let js_url = root_specifier_to_url("does-not-exist").unwrap(); + let result = worker.execute_mod_async(&js_url, false).wait(); + assert!(result.is_err()); + }) } #[test] fn execute_mod_002_hello() { - // This assumes cwd is project root (an assumption made throughout the - // tests). - let worker = create_test_worker(); - let js_url = root_specifier_to_url("./tests/002_hello.ts").unwrap(); - let result = worker.execute_mod_async(&js_url, false).wait(); - assert!(result.is_ok()); + tokio_util::init(|| { + // This assumes cwd is project root (an assumption made throughout the + // tests). + let worker = create_test_worker(); + let js_url = root_specifier_to_url("./tests/002_hello.ts").unwrap(); + let result = worker.execute_mod_async(&js_url, false).wait(); + assert!(result.is_ok()); + }) } } |