summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2019-05-20 12:06:57 -0400
committerRyan Dahl <ry@tinyclouds.org>2019-05-29 07:53:39 -0400
commit856c44213b7faf507d4b481cfc170b2fd08f971a (patch)
treeb2971883b0aeb43437a9be0076b4ffacde55d5b8 /cli
parent64d2b7bc90cf6fdba661d6d3fe243fe332c076ee (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.rs283
-rw-r--r--cli/msg.fbs32
-rw-r--r--cli/ops.rs106
-rw-r--r--cli/state.rs18
-rw-r--r--cli/worker.rs88
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());
+ })
}
}