Skip to content

Commit

Permalink
Add support for custom tsconfig.json (#2089)
Browse files Browse the repository at this point in the history
Use `--config`
  • Loading branch information
kitsonk authored and ry committed Apr 29, 2019
1 parent 73be183 commit 1a0f53a
Show file tree
Hide file tree
Showing 14 changed files with 438 additions and 25 deletions.
3 changes: 2 additions & 1 deletion cli/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ ts_sources = [
"../js/buffer.ts",
"../js/build.ts",
"../js/chmod.ts",
"../js/console_table.ts",
"../js/colors.ts",
"../js/compiler.ts",
"../js/console.ts",
"../js/console_table.ts",
"../js/copy_file.ts",
"../js/core.ts",
"../js/custom_event.ts",
Expand Down
25 changes: 25 additions & 0 deletions cli/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,23 @@ fn req(specifier: &str, referrer: &str, cmd_id: u32) -> Buf {
.into_boxed_bytes()
}

/// Returns an optional tuple which represents the state of the compiler
/// configuration where the first is canonical name for the configuration file
/// and a vector of the bytes of the contents of the configuration file.
pub fn get_compiler_config(
parent_state: &ThreadSafeState,
_compiler_type: &str,
) -> Option<(String, Vec<u8>)> {
// 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) {
(Some(config_path), Some(config)) => {
Some((config_path.to_string(), config.to_vec()))
}
_ => None,
}
}

pub fn compile_async(
parent_state: ThreadSafeState,
specifier: &str,
Expand Down Expand Up @@ -306,4 +323,12 @@ mod tests {

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();
let out = get_compiler_config(&state, compiler_type);
assert_eq!(out, None);
}
}
83 changes: 67 additions & 16 deletions cli/deno_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,18 @@ pub struct DenoDir {
// This splits to http and https deps
pub deps_http: PathBuf,
pub deps_https: PathBuf,
/// The active configuration file contents (or empty array) which applies to
/// source code cached by `DenoDir`.
pub config: Vec<u8>,
}

impl DenoDir {
// Must be called before using any function from this module.
// https://github.com/denoland/deno/blob/golang/deno_dir.go#L99-L111
pub fn new(custom_root: Option<PathBuf>) -> std::io::Result<Self> {
pub fn new(
custom_root: Option<PathBuf>,
state_config: &Option<Vec<u8>>,
) -> std::io::Result<Self> {
// Only setup once.
let home_dir = dirs::home_dir().expect("Could not get home directory.");
let fallback = home_dir.join(".deno");
Expand All @@ -73,12 +79,22 @@ impl DenoDir {
let deps_http = deps.join("http");
let deps_https = deps.join("https");

// Internally within DenoDir, we use the config as part of the hash to
// determine if a file has been transpiled with the same configuration, but
// we have borrowed the `State` configuration, which we want to either clone
// or create an empty `Vec` which we will use in our hash function.
let config = match state_config {
Some(config) => config.clone(),
_ => b"".to_vec(),
};

let deno_dir = Self {
root,
gen,
deps,
deps_http,
deps_https,
config,
};

// TODO Lazily create these directories.
Expand All @@ -102,7 +118,8 @@ impl DenoDir {
filename: &str,
source_code: &[u8],
) -> (PathBuf, PathBuf) {
let cache_key = source_code_hash(filename, source_code, version::DENO);
let cache_key =
source_code_hash(filename, source_code, version::DENO, &self.config);
(
self.gen.join(cache_key.to_string() + ".js"),
self.gen.join(cache_key.to_string() + ".js.map"),
Expand Down Expand Up @@ -156,6 +173,11 @@ impl DenoDir {

let gen = self.gen.clone();

// If we don't clone the config, we then end up creating an implied lifetime
// which gets returned in the future, so we clone here so as to not leak the
// move below when the future is resolving.
let config = self.config.clone();

Either::B(
get_source_code_async(
self,
Expand Down Expand Up @@ -191,8 +213,12 @@ impl DenoDir {
return Ok(out);
}

let cache_key =
source_code_hash(&out.filename, &out.source_code, version::DENO);
let cache_key = source_code_hash(
&out.filename,
&out.source_code,
version::DENO,
&config,
);
let (output_code_filename, output_source_map_filename) = (
gen.join(cache_key.to_string() + ".js"),
gen.join(cache_key.to_string() + ".js.map"),
Expand Down Expand Up @@ -468,15 +494,19 @@ fn load_cache2(
Ok((read_output_code, read_source_map))
}

/// Generate an SHA1 hash for source code, to be used to determine if a cached
/// version of the code is valid or invalid.
fn source_code_hash(
filename: &str,
source_code: &[u8],
version: &str,
config: &[u8],
) -> String {
let mut ctx = ring::digest::Context::new(&ring::digest::SHA1);
ctx.update(version.as_bytes());
ctx.update(filename.as_bytes());
ctx.update(source_code);
ctx.update(config);
let digest = ctx.finish();
let mut out = String::new();
// TODO There must be a better way to do this...
Expand Down Expand Up @@ -860,8 +890,9 @@ mod tests {

fn test_setup() -> (TempDir, DenoDir) {
let temp_dir = TempDir::new().expect("tempdir fail");
let deno_dir =
DenoDir::new(Some(temp_dir.path().to_path_buf())).expect("setup fail");
let config = Some(b"{}".to_vec());
let deno_dir = DenoDir::new(Some(temp_dir.path().to_path_buf()), &config)
.expect("setup fail");
(temp_dir, deno_dir)
}

Expand Down Expand Up @@ -904,7 +935,8 @@ mod tests {
let (temp_dir, deno_dir) = test_setup();
let filename = "hello.js";
let source_code = b"1+2";
let hash = source_code_hash(filename, source_code, version::DENO);
let config = b"{}";
let hash = source_code_hash(filename, source_code, version::DENO, config);
assert_eq!(
(
temp_dir.path().join(format!("gen/{}.js", hash)),
Expand All @@ -914,6 +946,24 @@ mod tests {
);
}

#[test]
fn test_cache_path_config() {
// We are changing the compiler config from the "mock" and so we expect the
// resolved files coming back to not match the calculated hash.
let (temp_dir, deno_dir) = test_setup();
let filename = "hello.js";
let source_code = b"1+2";
let config = b"{\"compilerOptions\":{}}";
let hash = source_code_hash(filename, source_code, version::DENO, config);
assert_ne!(
(
temp_dir.path().join(format!("gen/{}.js", hash)),
temp_dir.path().join(format!("gen/{}.js.map", hash))
),
deno_dir.cache_path(filename, source_code)
);
}

#[test]
fn test_code_cache() {
let (_temp_dir, deno_dir) = test_setup();
Expand All @@ -922,7 +972,8 @@ mod tests {
let source_code = b"1+2";
let output_code = b"1+2 // output code";
let source_map = b"{}";
let hash = source_code_hash(filename, source_code, version::DENO);
let config = b"{}";
let hash = source_code_hash(filename, source_code, version::DENO, config);
let (cache_path, source_map_path) =
deno_dir.cache_path(filename, source_code);
assert!(cache_path.ends_with(format!("gen/{}.js", hash)));
Expand All @@ -949,23 +1000,23 @@ mod tests {
#[test]
fn test_source_code_hash() {
assert_eq!(
"7e44de2ed9e0065da09d835b76b8d70be503d276",
source_code_hash("hello.ts", b"1+2", "0.2.11")
"830c8b63ba3194cf2108a3054c176b2bf53aee45",
source_code_hash("hello.ts", b"1+2", "0.2.11", b"{}")
);
// Different source_code should result in different hash.
assert_eq!(
"57033366cf9db1ef93deca258cdbcd9ef5f4bde1",
source_code_hash("hello.ts", b"1", "0.2.11")
"fb06127e9b2e169bea9c697fa73386ae7c901e8b",
source_code_hash("hello.ts", b"1", "0.2.11", b"{}")
);
// Different filename should result in different hash.
assert_eq!(
"19657f90b5b0540f87679e2fb362e7bd62b644b0",
source_code_hash("hi.ts", b"1+2", "0.2.11")
"3a17b6a493ff744b6a455071935f4bdcd2b72ec7",
source_code_hash("hi.ts", b"1+2", "0.2.11", b"{}")
);
// Different version should result in different hash.
assert_eq!(
"e2b4b7162975a02bf2770f16836eb21d5bcb8be1",
source_code_hash("hi.ts", b"1+2", "0.2.0")
"d6b2cfdc39dae9bd3ad5b493ee1544eb22e7475f",
source_code_hash("hi.ts", b"1+2", "0.2.0", b"{}")
);
}

Expand Down
24 changes: 24 additions & 0 deletions cli/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub struct DenoFlags {
pub log_debug: bool,
pub version: bool,
pub reload: bool,
/// When the `--config`/`-c` flag is used to pass the name, this will be set
/// the path passed on the command line, otherwise `None`.
pub config_path: Option<String>,
pub allow_read: bool,
pub allow_write: bool,
pub allow_net: bool,
Expand Down Expand Up @@ -79,6 +82,13 @@ pub fn create_cli_app<'a, 'b>() -> App<'a, 'b> {
.short("r")
.long("reload")
.help("Reload source code cache (recompile TypeScript)"),
).arg(
Arg::with_name("config")
.short("c")
.long("config")
.value_name("FILE")
.help("Load compiler configuration file")
.takes_value(true),
).arg(
Arg::with_name("v8-options")
.long("v8-options")
Expand Down Expand Up @@ -146,6 +156,7 @@ pub fn parse_flags(matches: ArgMatches) -> DenoFlags {
if matches.is_present("reload") {
flags.reload = true;
}
flags.config_path = matches.value_of("config").map(ToOwned::to_owned);
if matches.is_present("allow-read") {
flags.allow_read = true;
}
Expand Down Expand Up @@ -353,4 +364,17 @@ mod tests {
}
)
}

#[test]
fn test_set_flags_11() {
let flags =
flags_from_vec(svec!["deno", "-c", "tsconfig.json", "script.ts"]);
assert_eq!(
flags,
DenoFlags {
config_path: Some("tsconfig.json".to_owned()),
..DenoFlags::default()
}
)
}
}
11 changes: 11 additions & 0 deletions cli/msg.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ union Any {
Chdir,
Chmod,
Close,
CompilerConfig,
CompilerConfigRes,
CopyFile,
Cwd,
CwdRes,
Expand Down Expand Up @@ -174,6 +176,15 @@ table StartRes {
no_color: bool;
}

table CompilerConfig {
compiler_type: string;
}

table CompilerConfigRes {
path: string;
data: [ubyte];
}

table FormatError {
error: string;
}
Expand Down
38 changes: 38 additions & 0 deletions cli/ops.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use atty;
use crate::ansi;
use crate::compiler::get_compiler_config;
use crate::errors;
use crate::errors::{DenoError, DenoResult, ErrorKind};
use crate::fs as deno_fs;
Expand Down Expand Up @@ -146,6 +147,8 @@ pub fn dispatch_all(

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),
Expand Down Expand Up @@ -443,6 +446,41 @@ fn op_fetch_module_meta_data(
}()))
}

/// Retrieve any relevant compiler configuration.
fn op_compiler_config(
state: &ThreadSafeState,
base: &msg::Base<'_>,
data: deno_buf,
) -> Box<OpWithError> {
assert_eq!(data.len(), 0);
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(), "".as_bytes().to_owned()),
};
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<'_>,
Expand Down
Loading

0 comments on commit 1a0f53a

Please sign in to comment.