summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKitson Kelly <me@kitsonkelly.com>2020-09-29 17:16:12 +1000
committerGitHub <noreply@github.com>2020-09-29 17:16:12 +1000
commitb014a98534ca10283965a7996fc4e6a5f0dec421 (patch)
treecca34dae75f4b73d0d08381ba9b31d963724f146
parent970d412a0830a06cdd75b49d2c16dcc933af382a (diff)
refactor: improve graph and tsc_config (#7747)
-rw-r--r--cli/global_state.rs30
-rw-r--r--cli/graph.rs86
-rw-r--r--cli/tests/config.ts.out2
-rw-r--r--cli/tests/module_graph/tsconfig.json6
-rw-r--r--cli/tsc.rs20
-rw-r--r--cli/tsc_config.rs107
6 files changed, 147 insertions, 104 deletions
diff --git a/cli/global_state.rs b/cli/global_state.rs
index 3fe755ff1..ec2d78130 100644
--- a/cli/global_state.rs
+++ b/cli/global_state.rs
@@ -21,8 +21,6 @@ use deno_core::error::AnyError;
use deno_core::ModuleSpecifier;
use std::cell::RefCell;
use std::env;
-use std::fs;
-use std::io;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::Mutex;
@@ -134,37 +132,17 @@ impl GlobalState {
builder.insert(&module_specifier).await?;
let mut graph = builder.get_graph(&self.lockfile)?;
- // TODO(kitsonk) this needs to move, but CompilerConfig is way too
- // complicated to use here.
- let maybe_config = if let Some(path) = self.flags.config_path.clone() {
- let cwd = std::env::current_dir()?;
- let config_file = cwd.join(path);
- let config_path = config_file.canonicalize().map_err(|_| {
- io::Error::new(
- io::ErrorKind::InvalidInput,
- format!(
- "Could not find the config file: {}",
- config_file.to_string_lossy()
- ),
- )
- })?;
- let config_str = fs::read_to_string(config_path)?;
-
- Some(config_str)
- } else {
- None
- };
-
let (stats, maybe_ignored_options) =
graph.transpile(TranspileOptions {
debug: self.flags.log_level == Some(log::Level::Debug),
- maybe_config,
+ maybe_config_path: self.flags.config_path.clone(),
})?;
- debug!("{}", stats);
if let Some(ignored_options) = maybe_ignored_options {
- println!("Some compiler options were ignored:\n {}", ignored_options);
+ eprintln!("{}", ignored_options);
}
+
+ debug!("{}", stats);
} else {
let mut module_graph_loader = ModuleGraphLoader::new(
self.file_fetcher.clone(),
diff --git a/cli/graph.rs b/cli/graph.rs
index ccc7751e4..9025df185 100644
--- a/cli/graph.rs
+++ b/cli/graph.rs
@@ -14,14 +14,12 @@ use crate::specifier_handler::EmitMap;
use crate::specifier_handler::EmitType;
use crate::specifier_handler::FetchFuture;
use crate::specifier_handler::SpecifierHandler;
-use crate::tsc_config::json_merge;
-use crate::tsc_config::parse_config;
use crate::tsc_config::IgnoredCompilerOptions;
+use crate::tsc_config::TsConfig;
use crate::AnyError;
use deno_core::futures::stream::FuturesUnordered;
use deno_core::futures::stream::StreamExt;
-use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::ModuleSpecifier;
use regex::Regex;
@@ -38,8 +36,6 @@ use std::sync::Mutex;
use std::time::Instant;
use swc_ecmascript::dep_graph::DependencyKind;
-type Result<V> = result::Result<V, AnyError>;
-
pub type BuildInfoMap = HashMap<EmitType, TextDocument>;
lazy_static! {
@@ -123,7 +119,7 @@ pub trait ModuleProvider {
&self,
specifier: &str,
referrer: &ModuleSpecifier,
- ) -> Result<(ModuleSpecifier, MediaType)>;
+ ) -> Result<(ModuleSpecifier, MediaType), AnyError>;
}
/// An enum which represents the parsed out values of references in source code.
@@ -237,7 +233,7 @@ impl Module {
self.is_hydrated = true;
}
- pub fn parse(&mut self) -> Result<()> {
+ pub fn parse(&mut self) -> Result<(), AnyError> {
let parsed_module =
parse(&self.specifier, &self.source.to_str()?, &self.media_type)?;
@@ -318,7 +314,7 @@ impl Module {
&self,
specifier: &str,
maybe_location: Option<Location>,
- ) -> Result<ModuleSpecifier> {
+ ) -> Result<ModuleSpecifier, AnyError> {
let maybe_resolve = if let Some(import_map) = self.maybe_import_map.clone()
{
import_map
@@ -385,22 +381,10 @@ impl fmt::Display for Stats {
pub struct TranspileOptions {
/// If `true` then debug logging will be output from the isolate.
pub debug: bool,
- /// A string of configuration data that augments the the default configuration
- /// passed to the TypeScript compiler. This is typically the contents of a
- /// user supplied `tsconfig.json`.
- pub maybe_config: Option<String>,
-}
-
-/// The transpile options that are significant out of a user provided tsconfig
-/// file, that we want to deserialize out of the final config for a transpile.
-#[derive(Debug, Deserialize)]
-#[serde(rename_all = "camelCase")]
-struct TranspileConfigOptions {
- pub check_js: bool,
- pub emit_decorator_metadata: bool,
- pub jsx: String,
- pub jsx_factory: String,
- pub jsx_fragment_factory: String,
+ /// An optional string that points to a user supplied TypeScript configuration
+ /// file that augments the the default configuration passed to the TypeScript
+ /// compiler.
+ pub maybe_config_path: Option<String>,
}
/// A dependency graph of modules, were the modules that have been inserted via
@@ -432,7 +416,7 @@ impl Graph {
/// Update the handler with any modules that are marked as _dirty_ and update
/// any build info if present.
- fn flush(&mut self, emit_type: &EmitType) -> Result<()> {
+ fn flush(&mut self, emit_type: &EmitType) -> Result<(), AnyError> {
let mut handler = self.handler.borrow_mut();
for (_, module) in self.modules.iter_mut() {
if module.is_dirty {
@@ -462,7 +446,10 @@ impl Graph {
/// Verify the subresource integrity of the graph based upon the optional
/// lockfile, updating the lockfile with any missing resources. This will
/// error if any of the resources do not match their lock status.
- pub fn lock(&self, maybe_lockfile: &Option<Mutex<Lockfile>>) -> Result<()> {
+ pub fn lock(
+ &self,
+ maybe_lockfile: &Option<Mutex<Lockfile>>,
+ ) -> Result<(), AnyError> {
if let Some(lf) = maybe_lockfile {
let mut lockfile = lf.lock().unwrap();
for (ms, module) in self.modules.iter() {
@@ -493,28 +480,22 @@ impl Graph {
pub fn transpile(
&mut self,
options: TranspileOptions,
- ) -> Result<(Stats, Option<IgnoredCompilerOptions>)> {
+ ) -> Result<(Stats, Option<IgnoredCompilerOptions>), AnyError> {
let start = Instant::now();
let emit_type = EmitType::Cli;
- let mut compiler_options = json!({
+
+ let mut ts_config = TsConfig::new(json!({
"checkJs": false,
"emitDecoratorMetadata": false,
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
- });
+ }));
- let maybe_ignored_options = if let Some(config_text) = options.maybe_config
- {
- let (user_config, ignored_options) = parse_config(&config_text)?;
- json_merge(&mut compiler_options, &user_config);
- ignored_options
- } else {
- None
- };
+ let maybe_ignored_options =
+ ts_config.merge_user_config(options.maybe_config_path)?;
- let compiler_options: TranspileConfigOptions =
- serde_json::from_value(compiler_options)?;
+ let compiler_options = ts_config.as_transpile_config()?;
let check_js = compiler_options.check_js;
let transform_jsx = compiler_options.jsx == "react";
let emit_options = ast::TranspileOptions {
@@ -581,7 +562,7 @@ impl<'a> ModuleProvider for Graph {
&self,
specifier: &str,
referrer: &ModuleSpecifier,
- ) -> Result<(ModuleSpecifier, MediaType)> {
+ ) -> Result<(ModuleSpecifier, MediaType), AnyError> {
if !self.modules.contains_key(referrer) {
return Err(MissingSpecifier(referrer.to_owned()).into());
}
@@ -659,7 +640,7 @@ impl GraphBuilder {
/// Request a module to be fetched from the handler and queue up its future
/// to be awaited to be resolved.
- fn fetch(&mut self, specifier: &ModuleSpecifier) -> Result<()> {
+ fn fetch(&mut self, specifier: &ModuleSpecifier) -> Result<(), AnyError> {
if self.fetched.contains(&specifier) {
return Ok(());
}
@@ -674,7 +655,7 @@ impl GraphBuilder {
/// Visit a module that has been fetched, hydrating the module, analyzing its
/// dependencies if required, fetching those dependencies, and inserting the
/// module into the graph.
- fn visit(&mut self, cached_module: CachedModule) -> Result<()> {
+ fn visit(&mut self, cached_module: CachedModule) -> Result<(), AnyError> {
let specifier = cached_module.specifier.clone();
let mut module =
Module::new(specifier.clone(), self.maybe_import_map.clone());
@@ -711,7 +692,10 @@ impl GraphBuilder {
/// Insert a module into the graph based on a module specifier. The module
/// and any dependencies will be fetched from the handler. The module will
/// also be treated as a _root_ module in the graph.
- pub async fn insert(&mut self, specifier: &ModuleSpecifier) -> Result<()> {
+ pub async fn insert(
+ &mut self,
+ specifier: &ModuleSpecifier,
+ ) -> Result<(), AnyError> {
self.fetch(specifier)?;
loop {
@@ -736,7 +720,7 @@ impl GraphBuilder {
pub fn get_graph(
self,
maybe_lockfile: &Option<Mutex<Lockfile>>,
- ) -> Result<Graph> {
+ ) -> Result<Graph, AnyError> {
self.graph.lock(maybe_lockfile)?;
Ok(self.graph)
}
@@ -902,7 +886,7 @@ mod tests {
let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
let fixtures = c.join("tests/module_graph");
let handler = Rc::new(RefCell::new(MockSpecifierHandler {
- fixtures,
+ fixtures: fixtures.clone(),
..MockSpecifierHandler::default()
}));
let mut builder = GraphBuilder::new(handler.clone(), None);
@@ -914,21 +898,15 @@ mod tests {
.await
.expect("module not inserted");
let mut graph = builder.get_graph(&None).expect("could not get graph");
- let config = r#"{
- "compilerOptions": {
- "target": "es5",
- "jsx": "preserve"
- }
- }"#;
let (_, maybe_ignored_options) = graph
.transpile(TranspileOptions {
debug: false,
- maybe_config: Some(config.to_string()),
+ maybe_config_path: Some("tests/module_graph/tsconfig.json".to_string()),
})
.unwrap();
assert_eq!(
- maybe_ignored_options,
- Some(IgnoredCompilerOptions(vec!["target".to_string()])),
+ maybe_ignored_options.unwrap().items,
+ vec!["target".to_string()],
"the 'target' options should have been ignored"
);
let h = handler.borrow();
diff --git a/cli/tests/config.ts.out b/cli/tests/config.ts.out
index 8f5cf7e39..9840dba2e 100644
--- a/cli/tests/config.ts.out
+++ b/cli/tests/config.ts.out
@@ -1,4 +1,4 @@
-[WILDCARD]Unsupported compiler options in "[WILDCARD]config.tsconfig.json"
+[WILDCARD]Unsupported compiler options in "[WILDCARD]config.tsconfig.json".
The following options were ignored:
module, target
error: TS2532 [ERROR]: Object is possibly 'undefined'.
diff --git a/cli/tests/module_graph/tsconfig.json b/cli/tests/module_graph/tsconfig.json
new file mode 100644
index 000000000..a4c5f4f33
--- /dev/null
+++ b/cli/tests/module_graph/tsconfig.json
@@ -0,0 +1,6 @@
+{
+ "compilerOptions": {
+ "target": "ES5",
+ "jsx": "preserve"
+ }
+}
diff --git a/cli/tsc.rs b/cli/tsc.rs
index 245247f13..9b944125f 100644
--- a/cli/tsc.rs
+++ b/cli/tsc.rs
@@ -41,7 +41,6 @@ use std::collections::HashSet;
use std::fs;
use std::io;
use std::ops::Deref;
-use std::path::Path;
use std::path::PathBuf;
use std::str;
use std::sync::Arc;
@@ -141,14 +140,9 @@ lazy_static! {
fn warn_ignored_options(
maybe_ignored_options: Option<tsc_config::IgnoredCompilerOptions>,
- config_path: &Path,
) {
if let Some(ignored_options) = maybe_ignored_options {
- eprintln!(
- "Unsupported compiler options in \"{}\"\n The following options were ignored:\n {}",
- config_path.to_string_lossy(),
- ignored_options
- );
+ eprintln!("{}", ignored_options);
}
}
@@ -210,7 +204,7 @@ impl CompilerConfig {
let (options, maybe_ignored_options) = if config_str.is_empty() {
(json!({}), None)
} else {
- tsc_config::parse_config(&config_str)?
+ tsc_config::parse_config(&config_str, &config_path)?
};
// If `checkJs` is set to true in `compilerOptions` then we're gonna be compiling
@@ -526,10 +520,7 @@ impl TsCompiler {
tsc_config::json_merge(&mut compiler_options, &compiler_config.options);
- warn_ignored_options(
- compiler_config.maybe_ignored_options,
- compiler_config.path.as_ref().unwrap(),
- );
+ warn_ignored_options(compiler_config.maybe_ignored_options);
let j = json!({
"type": CompilerRequestType::Compile,
@@ -646,10 +637,7 @@ impl TsCompiler {
tsc_config::json_merge(&mut compiler_options, &compiler_config.options);
- warn_ignored_options(
- compiler_config.maybe_ignored_options,
- compiler_config.path.as_ref().unwrap(),
- );
+ warn_ignored_options(compiler_config.maybe_ignored_options);
let j = json!({
"type": CompilerRequestType::Bundle,
diff --git a/cli/tsc_config.rs b/cli/tsc_config.rs
index f748c7a5c..b52ed2abd 100644
--- a/cli/tsc_config.rs
+++ b/cli/tsc_config.rs
@@ -5,19 +5,40 @@ use deno_core::serde_json;
use deno_core::serde_json::Value;
use jsonc_parser::JsonValue;
use serde::Deserialize;
+use serde::Serialize;
+use serde::Serializer;
use std::collections::HashMap;
use std::fmt;
+use std::path::Path;
+use std::path::PathBuf;
use std::str::FromStr;
-#[derive(Clone, Debug, PartialEq)]
-pub struct IgnoredCompilerOptions(pub Vec<String>);
+/// The transpile options that are significant out of a user provided tsconfig
+/// file, that we want to deserialize out of the final config for a transpile.
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct TranspileConfigOptions {
+ pub check_js: bool,
+ pub emit_decorator_metadata: bool,
+ pub jsx: String,
+ pub jsx_factory: String,
+ pub jsx_fragment_factory: String,
+}
+
+/// A structure that represents a set of options that were ignored and the
+/// path those options came from.
+#[derive(Debug, Clone, PartialEq)]
+pub struct IgnoredCompilerOptions {
+ pub items: Vec<String>,
+ pub path: PathBuf,
+}
impl fmt::Display for IgnoredCompilerOptions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let mut codes = self.0.clone();
+ let mut codes = self.items.clone();
codes.sort();
- write!(f, "{}", codes.join(", "))
+ write!(f, "Unsupported compiler options in \"{}\".\n The following options were ignored:\n {}", self.path.to_string_lossy(), codes.join(", "))
}
}
@@ -149,6 +170,7 @@ pub fn parse_raw_config(config_text: &str) -> Result<Value, AnyError> {
/// 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_value(config_text)?.unwrap();
@@ -167,7 +189,10 @@ pub fn parse_config(
}
let options_value = serde_json::to_value(compiler_options)?;
let ignored_options = if !items.is_empty() {
- Some(IgnoredCompilerOptions(items))
+ Some(IgnoredCompilerOptions {
+ items,
+ path: path.to_path_buf(),
+ })
} else {
None
};
@@ -175,6 +200,70 @@ pub fn parse_config(
Ok((options_value, ignored_options))
}
+/// A structure for managing the configuration of TypeScript
+#[derive(Debug, Clone)]
+pub struct TsConfig(Value);
+
+impl TsConfig {
+ /// Create a new `TsConfig` with the base being the `value` supplied.
+ pub fn new(value: Value) -> Self {
+ TsConfig(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
+ /// 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_user_config(
+ &mut self,
+ maybe_path: Option<String>,
+ ) -> 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 = config_file.canonicalize().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);
+
+ Ok(maybe_ignored_options)
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Return the current configuration as a `TranspileConfigOptions` structure.
+ pub fn as_transpile_config(
+ &self,
+ ) -> Result<TranspileConfigOptions, AnyError> {
+ let options: TranspileConfigOptions =
+ serde_json::from_value(self.0.clone())?;
+ Ok(options)
+ }
+}
+
+impl Serialize for TsConfig {
+ /// Serializes inner hash map which is ordered by the key
+ fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ Serialize::serialize(&self.0, serializer)
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -210,15 +299,19 @@ mod tests {
"strict": true
}
}"#;
+ let config_path = PathBuf::from("/deno/tsconfig.json");
let (options_value, ignored) =
- parse_config(config_text).expect("error parsing");
+ parse_config(config_text, &config_path).expect("error parsing");
assert!(options_value.is_object());
let options = options_value.as_object().unwrap();
assert!(options.contains_key("strict"));
assert_eq!(options.len(), 1);
assert_eq!(
ignored,
- Some(IgnoredCompilerOptions(vec!["build".to_string()])),
+ Some(IgnoredCompilerOptions {
+ items: vec!["build".to_string()],
+ path: config_path,
+ }),
);
}