1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 15:24:46 -05:00

refactor(cli): replace loading file for --config flag with generic structure (#10481)

Currently file passed to --config file is parsed using TsConfig structure
that does multiple things when loading the file. Instead of relying on that
structure I've introduced ConfigFile structure that can be updated to
sniff out more fields from the config file in the future.
This commit is contained in:
Bartek Iwańczuk 2021-05-10 18:16:39 +02:00 committed by GitHub
parent 7fc211e627
commit ce48b32979
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 183 additions and 153 deletions

View file

@ -1,7 +1,7 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use crate::config_file;
use crate::media_type::MediaType;
use crate::tsc_config;
use deno_core::error::AnyError;
use deno_core::resolve_url_or_path;
@ -229,9 +229,9 @@ impl Default for EmitOptions {
}
}
impl From<tsc_config::TsConfig> for EmitOptions {
fn from(config: tsc_config::TsConfig) -> Self {
let options: tsc_config::EmitConfigOptions =
impl From<config_file::TsConfig> for EmitOptions {
fn from(config: config_file::TsConfig) -> Self {
let options: config_file::EmitConfigOptions =
serde_json::from_value(config.0).unwrap();
let imports_not_used_as_values =
match options.imports_not_used_as_values.as_str() {

View file

@ -2,6 +2,7 @@
use crate::fs_util::canonicalize_path;
use deno_core::error::AnyError;
use deno_core::error::Context;
use deno_core::serde::Deserialize;
use deno_core::serde::Serialize;
use deno_core::serde::Serializer;
@ -147,19 +148,6 @@ pub fn json_merge(a: &mut Value, b: &Value) {
}
}
/// A structure for deserializing a `tsconfig.json` file for the purposes of
/// being used internally within Deno.
///
/// The only key in the JSON object that Deno cares about is the
/// `compilerOptions` property. A valid `tsconfig.json` file can also contain
/// the keys `exclude`, `extends`, `files`, `include`, `references`, and
/// `typeAcquisition` which are all "ignored" by Deno.
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct TsConfigJson {
compiler_options: Option<HashMap<String, Value>>,
}
fn parse_compiler_options(
compiler_options: &HashMap<String, Value>,
maybe_path: Option<PathBuf>,
@ -188,23 +176,6 @@ fn parse_compiler_options(
Ok((value, maybe_ignored_options))
}
/// Take a string of JSONC, parse it and return a serde `Value` of the text.
/// The result also contains any options that were ignored.
pub fn parse_config(
config_text: &str,
path: &Path,
) -> Result<(Value, Option<IgnoredCompilerOptions>), AnyError> {
assert!(!config_text.is_empty());
let jsonc = jsonc_parser::parse_to_serde_value(config_text)?.unwrap();
let config: TsConfigJson = serde_json::from_value(jsonc)?;
if let Some(compiler_options) = config.compiler_options {
parse_compiler_options(&compiler_options, Some(path.to_owned()), false)
} else {
Ok((json!({}), None))
}
}
/// A structure for managing the configuration of TypeScript
#[derive(Debug, Clone)]
pub struct TsConfig(pub Value);
@ -245,34 +216,17 @@ impl TsConfig {
json_merge(&mut self.0, value);
}
/// Take an optional string representing a user provided TypeScript config file
/// which was passed in via the `--config` compiler option and merge it with
/// Take an optional user provided config file
/// which was passed in via the `--config` flag and merge `compilerOptions` with
/// the configuration. Returning the result which optionally contains any
/// compiler options that were ignored.
///
/// When there are options ignored out of the file, a warning will be written
/// to stderr regarding the options that were ignored.
pub fn merge_tsconfig(
pub fn merge_tsconfig_from_config_file(
&mut self,
maybe_path: Option<String>,
maybe_config_file: Option<&ConfigFile>,
) -> Result<Option<IgnoredCompilerOptions>, AnyError> {
if let Some(path) = maybe_path {
let cwd = std::env::current_dir()?;
let config_file = cwd.join(path);
let config_path = canonicalize_path(&config_file).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!(
"Could not find the config file: {}",
config_file.to_string_lossy()
),
)
})?;
let config_text = std::fs::read_to_string(config_path.clone())?;
let (value, maybe_ignored_options) =
parse_config(&config_text, &config_path)?;
json_merge(&mut self.0, &value);
if let Some(config_file) = maybe_config_file {
let (value, maybe_ignored_options) = config_file.as_compiler_options()?;
self.merge(&value);
Ok(maybe_ignored_options)
} else {
Ok(None)
@ -288,7 +242,7 @@ impl TsConfig {
) -> Result<Option<IgnoredCompilerOptions>, AnyError> {
let (value, maybe_ignored_options) =
parse_compiler_options(user_options, None, true)?;
json_merge(&mut self.0, &value);
self.merge(&value);
Ok(maybe_ignored_options)
}
}
@ -303,11 +257,73 @@ impl Serialize for TsConfig {
}
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ConfigFileJson {
pub compiler_options: Option<Value>,
}
#[derive(Clone, Debug)]
pub struct ConfigFile {
pub path: PathBuf,
pub json: ConfigFileJson,
}
impl ConfigFile {
pub fn read(path: &str) -> Result<Self, AnyError> {
let cwd = std::env::current_dir()?;
let config_file = cwd.join(path);
let config_path = canonicalize_path(&config_file).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!(
"Could not find the config file: {}",
config_file.to_string_lossy()
),
)
})?;
let config_text = std::fs::read_to_string(config_path.clone())?;
Self::new(&config_text, &config_path)
}
pub fn new(text: &str, path: &Path) -> Result<Self, AnyError> {
let jsonc = jsonc_parser::parse_to_serde_value(text)?.unwrap();
let json: ConfigFileJson = serde_json::from_value(jsonc)?;
Ok(Self {
path: path.to_owned(),
json,
})
}
/// Parse `compilerOptions` and return a serde `Value`.
/// The result also contains any options that were ignored.
pub fn as_compiler_options(
&self,
) -> Result<(Value, Option<IgnoredCompilerOptions>), AnyError> {
if let Some(compiler_options) = self.json.compiler_options.clone() {
let options: HashMap<String, Value> =
serde_json::from_value(compiler_options)
.context("compilerOptions should be an object")?;
parse_compiler_options(&options, Some(self.path.to_owned()), false)
} else {
Ok((json!({}), None))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use deno_core::serde_json::json;
#[test]
fn read_config_file() {
let config_file = ConfigFile::read("tests/module_graph/tsconfig.json")
.expect("Failed to load config file");
assert!(config_file.json.compiler_options.is_some());
}
#[test]
fn test_json_merge() {
let mut value_a = json!({
@ -339,8 +355,9 @@ mod tests {
}
}"#;
let config_path = PathBuf::from("/deno/tsconfig.json");
let config_file = ConfigFile::new(config_text, &config_path).unwrap();
let (options_value, ignored) =
parse_config(config_text, &config_path).expect("error parsing");
config_file.as_compiler_options().expect("error parsing");
assert!(options_value.is_object());
let options = options_value.as_object().unwrap();
assert!(options.contains_key("strict"));

View file

@ -102,7 +102,7 @@ pub struct WorkspaceSettings {
/// A flag that indicates if Deno is enabled for the workspace.
pub enable: bool,
/// An option that points to a path string of the tsconfig file to apply to
/// An option that points to a path string of the config file to apply to
/// code within the workspace.
pub config: Option<String>,

View file

@ -2,6 +2,7 @@
use deno_core::error::anyhow;
use deno_core::error::AnyError;
use deno_core::error::Context;
use deno_core::resolve_url;
use deno_core::serde::Deserialize;
use deno_core::serde::Serialize;
@ -28,11 +29,11 @@ use std::rc::Rc;
use std::sync::Arc;
use tokio::fs;
use crate::config_file::ConfigFile;
use crate::config_file::TsConfig;
use crate::deno_dir;
use crate::import_map::ImportMap;
use crate::media_type::MediaType;
use crate::tsc_config::parse_config;
use crate::tsc_config::TsConfig;
use super::analysis;
use super::analysis::ts_changes_to_edit;
@ -408,21 +409,10 @@ impl Inner {
config_str
))
}?;
let config_path = config_url
.to_file_path()
.map_err(|_| anyhow!("Bad file path."))?;
let config_text =
fs::read_to_string(config_path.clone())
.await
.map_err(|err| {
anyhow!(
"Failed to load the configuration file at: {}. [{}]",
config_url,
err
)
})?;
let (value, maybe_ignored_options) =
parse_config(&config_text, &config_path)?;
let config_file = ConfigFile::read(config_url.as_str())
.context("Failed to load configuration file")?;
let (value, maybe_ignored_options) = config_file.as_compiler_options()?;
tsconfig.merge(&value);
self.maybe_config_uri = Some(config_url);
if let Some(ignored_options) = maybe_ignored_options {

View file

@ -11,11 +11,11 @@ use super::semantic_tokens::TsTokenEncodingConsts;
use super::text;
use super::text::LineIndex;
use crate::config_file::TsConfig;
use crate::media_type::MediaType;
use crate::tokio_util::create_basic_runtime;
use crate::tsc;
use crate::tsc::ResolveArgs;
use crate::tsc_config::TsConfig;
use deno_core::error::anyhow;
use deno_core::error::custom_error;

View file

@ -4,6 +4,7 @@ mod ast;
mod auth_tokens;
mod checksum;
mod colors;
mod config_file;
mod deno_dir;
mod diagnostics;
mod diff;
@ -33,7 +34,6 @@ mod text_encoding;
mod tokio_util;
mod tools;
mod tsc;
mod tsc_config;
mod unix_util;
mod version;
@ -359,7 +359,8 @@ async fn compile_command(
colors::green("Bundle"),
module_specifier.to_string()
);
let bundle_str = bundle_module_graph(module_graph, flags, debug)?;
let bundle_str =
bundle_module_graph(module_graph, program_state.clone(), flags, debug)?;
info!(
"{} {}",
@ -565,7 +566,7 @@ async fn create_module_graph_and_maybe_check(
debug,
emit: false,
lib,
maybe_config_path: program_state.flags.config_path.clone(),
maybe_config_file: program_state.maybe_config_file.clone(),
reload: program_state.flags.reload,
})?;
@ -583,13 +584,14 @@ async fn create_module_graph_and_maybe_check(
fn bundle_module_graph(
module_graph: module_graph::Graph,
program_state: Arc<ProgramState>,
flags: Flags,
debug: bool,
) -> Result<String, AnyError> {
let (bundle, stats, maybe_ignored_options) =
module_graph.bundle(module_graph::BundleOptions {
debug,
maybe_config_path: flags.config_path,
maybe_config_file: program_state.maybe_config_file.clone(),
})?;
match maybe_ignored_options {
Some(ignored_options) if flags.no_check => {
@ -636,13 +638,15 @@ async fn bundle_command(
.push(fs_util::resolve_from_cwd(std::path::Path::new(import_map))?);
}
Ok((paths_to_watch, module_graph))
Ok((paths_to_watch, module_graph, program_state))
}
.map(move |result| match result {
Ok((paths_to_watch, module_graph)) => ResolutionResult::Restart {
paths_to_watch,
result: Ok(module_graph),
},
Ok((paths_to_watch, module_graph, program_state)) => {
ResolutionResult::Restart {
paths_to_watch,
result: Ok((program_state, module_graph)),
}
}
Err(e) => ResolutionResult::Restart {
paths_to_watch: vec![PathBuf::from(source_file2)],
result: Err(e),
@ -650,13 +654,17 @@ async fn bundle_command(
})
};
let operation = |module_graph: module_graph::Graph| {
let operation = |(program_state, module_graph): (
Arc<ProgramState>,
module_graph::Graph,
)| {
let flags = flags.clone();
let out_file = out_file.clone();
async move {
info!("{} {}", colors::green("Bundle"), module_graph.info()?.root);
let output = bundle_module_graph(module_graph, flags, debug)?;
let output =
bundle_module_graph(module_graph, program_state, flags, debug)?;
debug!(">>>>> bundle END");
@ -794,13 +802,15 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> {
.push(fs_util::resolve_from_cwd(std::path::Path::new(import_map))?);
}
Ok((paths_to_watch, main_module))
Ok((paths_to_watch, main_module, program_state))
}
.map(move |result| match result {
Ok((paths_to_watch, module_info)) => ResolutionResult::Restart {
paths_to_watch,
result: Ok(module_info),
},
Ok((paths_to_watch, module_info, program_state)) => {
ResolutionResult::Restart {
paths_to_watch,
result: Ok((program_state, module_info)),
}
}
Err(e) => ResolutionResult::Restart {
paths_to_watch: vec![PathBuf::from(script2)],
result: Err(e),
@ -808,26 +818,26 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> {
})
};
let operation = |main_module: ModuleSpecifier| {
let flags = flags.clone();
let permissions = Permissions::from_options(&flags.clone().into());
async move {
let main_module = main_module.clone();
let program_state = ProgramState::build(flags).await?;
let mut worker = create_main_worker(
&program_state,
main_module.clone(),
permissions,
false,
);
debug!("main_module {}", main_module);
worker.execute_module(&main_module).await?;
worker.execute("window.dispatchEvent(new Event('load'))")?;
worker.run_event_loop().await?;
worker.execute("window.dispatchEvent(new Event('unload'))")?;
Ok(())
}
};
let operation =
|(program_state, main_module): (Arc<ProgramState>, ModuleSpecifier)| {
let flags = flags.clone();
let permissions = Permissions::from_options(&flags.into());
async move {
let main_module = main_module.clone();
let mut worker = create_main_worker(
&program_state,
main_module.clone(),
permissions,
false,
);
debug!("main_module {}", main_module);
worker.execute_module(&main_module).await?;
worker.execute("window.dispatchEvent(new Event('load'))")?;
worker.run_event_loop().await?;
worker.execute("window.dispatchEvent(new Event('unload'))")?;
Ok(())
}
};
file_watcher::watch_func(resolver, operation, "Process").await
}

View file

@ -7,6 +7,9 @@ use crate::ast::Location;
use crate::ast::ParsedModule;
use crate::checksum;
use crate::colors;
use crate::config_file::ConfigFile;
use crate::config_file::IgnoredCompilerOptions;
use crate::config_file::TsConfig;
use crate::diagnostics::Diagnostics;
use crate::import_map::ImportMap;
use crate::info;
@ -19,8 +22,6 @@ use crate::specifier_handler::Emit;
use crate::specifier_handler::FetchFuture;
use crate::specifier_handler::SpecifierHandler;
use crate::tsc;
use crate::tsc_config::IgnoredCompilerOptions;
use crate::tsc_config::TsConfig;
use crate::version;
use deno_core::error::anyhow;
use deno_core::error::custom_error;
@ -579,10 +580,10 @@ impl Serialize for TypeLib {
pub struct BundleOptions {
/// If `true` then debug logging will be output from the isolate.
pub debug: bool,
/// An optional string that points to a user supplied TypeScript configuration
/// file that augments the the default configuration passed to the TypeScript
/// An optional config file with user supplied TypeScript configuration
/// that augments the the default configuration passed to the TypeScript
/// compiler.
pub maybe_config_path: Option<String>,
pub maybe_config_file: Option<ConfigFile>,
}
#[derive(Debug, Default)]
@ -593,10 +594,10 @@ pub struct CheckOptions {
pub emit: bool,
/// The base type libraries that should be used when type checking.
pub lib: TypeLib,
/// An optional string that points to a user supplied TypeScript configuration
/// file that augments the the default configuration passed to the TypeScript
/// An optional config file with user supplied TypeScript configuration
/// that augments the the default configuration passed to the TypeScript
/// compiler.
pub maybe_config_path: Option<String>,
pub maybe_config_file: Option<ConfigFile>,
/// Ignore any previously emits and ensure that all files are emitted from
/// source.
pub reload: bool,
@ -642,10 +643,10 @@ pub struct EmitOptions {
pub struct TranspileOptions {
/// If `true` then debug logging will be output from the isolate.
pub debug: bool,
/// An optional string that points to a user supplied TypeScript configuration
/// file that augments the the default configuration passed to the TypeScript
/// An optional config file with user supplied TypeScript configuration
/// that augments the the default configuration passed to the TypeScript
/// compiler.
pub maybe_config_path: Option<String>,
pub maybe_config_file: Option<ConfigFile>,
/// Ignore any previously emits and ensure that all files are emitted from
/// source.
pub reload: bool,
@ -773,8 +774,8 @@ impl Graph {
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
}));
let maybe_ignored_options =
ts_config.merge_tsconfig(options.maybe_config_path)?;
let maybe_ignored_options = ts_config
.merge_tsconfig_from_config_file(options.maybe_config_file.as_ref())?;
let s = self.emit_bundle(
&root_specifier,
@ -823,8 +824,8 @@ impl Graph {
"noEmit": true,
}));
}
let maybe_ignored_options =
config.merge_tsconfig(options.maybe_config_path)?;
let maybe_ignored_options = config
.merge_tsconfig_from_config_file(options.maybe_config_file.as_ref())?;
// Short circuit if none of the modules require an emit, or all of the
// modules that require an emit have a valid emit. There is also an edge
@ -1610,8 +1611,8 @@ impl Graph {
"jsxFragmentFactory": "React.Fragment",
}));
let maybe_ignored_options =
ts_config.merge_tsconfig(options.maybe_config_path)?;
let maybe_ignored_options = ts_config
.merge_tsconfig_from_config_file(options.maybe_config_file.as_ref())?;
let config = ts_config.as_bytes();
let emit_options: ast::EmitOptions = ts_config.into();
@ -2178,7 +2179,7 @@ pub mod tests {
debug: false,
emit: true,
lib: TypeLib::DenoWindow,
maybe_config_path: None,
maybe_config_file: None,
reload: false,
})
.expect("should have checked");
@ -2200,7 +2201,7 @@ pub mod tests {
debug: false,
emit: false,
lib: TypeLib::DenoWindow,
maybe_config_path: None,
maybe_config_file: None,
reload: false,
})
.expect("should have checked");
@ -2217,7 +2218,7 @@ pub mod tests {
debug: false,
emit: true,
lib: TypeLib::DenoWindow,
maybe_config_path: None,
maybe_config_file: None,
reload: false,
})
.expect("should have checked");
@ -2241,7 +2242,7 @@ pub mod tests {
debug: false,
emit: false,
lib: TypeLib::DenoWindow,
maybe_config_path: None,
maybe_config_file: None,
reload: false,
})
.expect("should have checked");
@ -2263,7 +2264,7 @@ pub mod tests {
debug: false,
emit: true,
lib: TypeLib::DenoWindow,
maybe_config_path: None,
maybe_config_file: None,
reload: false,
})
.expect("should have checked");
@ -2284,7 +2285,7 @@ pub mod tests {
debug: false,
emit: false,
lib: TypeLib::DenoWindow,
maybe_config_path: None,
maybe_config_file: None,
reload: false,
})
.expect("should have checked");
@ -2296,14 +2297,14 @@ pub mod tests {
let specifier = resolve_url_or_path("file:///tests/checkwithconfig.ts")
.expect("could not resolve module");
let (graph, handler) = setup(specifier.clone()).await;
let config_file =
ConfigFile::read("tests/module_graph/tsconfig_01.json").unwrap();
let result_info = graph
.check(CheckOptions {
debug: false,
emit: true,
lib: TypeLib::DenoWindow,
maybe_config_path: Some(
"tests/module_graph/tsconfig_01.json".to_string(),
),
maybe_config_file: Some(config_file),
reload: true,
})
.expect("should have checked");
@ -2317,14 +2318,14 @@ pub mod tests {
// let's do it all over again to ensure that the versions are determinstic
let (graph, handler) = setup(specifier).await;
let config_file =
ConfigFile::read("tests/module_graph/tsconfig_01.json").unwrap();
let result_info = graph
.check(CheckOptions {
debug: false,
emit: true,
lib: TypeLib::DenoWindow,
maybe_config_path: Some(
"tests/module_graph/tsconfig_01.json".to_string(),
),
maybe_config_file: Some(config_file),
reload: true,
})
.expect("should have checked");
@ -2545,10 +2546,12 @@ pub mod tests {
let specifier = resolve_url_or_path("https://deno.land/x/transpile.tsx")
.expect("could not resolve module");
let (mut graph, handler) = setup(specifier).await;
let config_file =
ConfigFile::read("tests/module_graph/tsconfig.json").unwrap();
let result_info = graph
.transpile(TranspileOptions {
debug: false,
maybe_config_path: Some("tests/module_graph/tsconfig.json".to_string()),
maybe_config_file: Some(config_file),
reload: false,
})
.unwrap();

View file

@ -1,5 +1,6 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use crate::config_file::ConfigFile;
use crate::deno_dir;
use crate::file_fetcher::CacheSetting;
use crate::file_fetcher::FileFetcher;
@ -46,6 +47,7 @@ pub struct ProgramState {
pub modules:
Arc<Mutex<HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>>>>,
pub lockfile: Option<Arc<Mutex<Lockfile>>>,
pub maybe_config_file: Option<ConfigFile>,
pub maybe_import_map: Option<ImportMap>,
pub maybe_inspector_server: Option<Arc<InspectorServer>>,
pub ca_data: Option<Vec<u8>>,
@ -91,6 +93,13 @@ impl ProgramState {
None
};
let maybe_config_file =
if let Some(config_path) = flags.config_path.as_ref() {
Some(ConfigFile::read(config_path)?)
} else {
None
};
let maybe_import_map: Option<ImportMap> =
match flags.import_map_path.as_ref() {
None => None,
@ -129,6 +138,7 @@ impl ProgramState {
file_fetcher,
modules: Default::default(),
lockfile,
maybe_config_file,
maybe_import_map,
maybe_inspector_server,
ca_data,
@ -160,12 +170,12 @@ impl ProgramState {
let mut graph = builder.get_graph();
let debug = self.flags.log_level == Some(log::Level::Debug);
let maybe_config_path = self.flags.config_path.clone();
let maybe_config_file = self.maybe_config_file.clone();
let result_modules = if self.flags.no_check {
let result_info = graph.transpile(TranspileOptions {
debug,
maybe_config_path,
maybe_config_file,
reload: self.flags.reload,
})?;
debug!("{}", result_info.stats);
@ -178,7 +188,7 @@ impl ProgramState {
debug,
emit: true,
lib,
maybe_config_path,
maybe_config_file,
reload: self.flags.reload,
})?;
@ -229,12 +239,12 @@ impl ProgramState {
builder.add(&specifier, is_dynamic).await?;
let mut graph = builder.get_graph();
let debug = self.flags.log_level == Some(log::Level::Debug);
let maybe_config_path = self.flags.config_path.clone();
let maybe_config_file = self.maybe_config_file.clone();
let result_modules = if self.flags.no_check {
let result_info = graph.transpile(TranspileOptions {
debug,
maybe_config_path,
maybe_config_file,
reload: self.flags.reload,
})?;
debug!("{}", result_info.stats);
@ -247,7 +257,7 @@ impl ProgramState {
debug,
emit: true,
lib,
maybe_config_path,
maybe_config_file,
reload: self.flags.reload,
})?;

View file

@ -1,10 +1,10 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use crate::config_file::TsConfig;
use crate::diagnostics::Diagnostics;
use crate::media_type::MediaType;
use crate::module_graph::Graph;
use crate::module_graph::Stats;
use crate::tsc_config::TsConfig;
use deno_core::error::anyhow;
use deno_core::error::bail;
@ -538,11 +538,11 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
#[cfg(test)]
mod tests {
use super::*;
use crate::config_file::TsConfig;
use crate::diagnostics::Diagnostic;
use crate::diagnostics::DiagnosticCategory;
use crate::module_graph::tests::MockSpecifierHandler;
use crate::module_graph::GraphBuilder;
use crate::tsc_config::TsConfig;
use std::env;
use std::path::PathBuf;
use std::sync::Mutex;